diff --git a/.deploy.lock b/.deploy.lock new file mode 100644 index 00000000..e69de29b diff --git a/Dockerfile b/Dockerfile index ef303449..a413e63a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,13 @@ RUN --mount=type=cache,id=epicnext-pnpm,target=/pnpm/store,sharing=locked \ # Source changes invalidate compilation, but keep the installed dependencies. COPY . . +# --- Automatische GitLab Pull --- +# Vraagt de meest recente commit op van gitlab.epicnabbo.nl (project: remco/EpicNext-Cms). +# Bij een nieuwe commit breekt Docker hier automatisch de cache en haalt hij direct de nieuwste code binnen. +# (remco%2FEpicNext-Cms is de URL-encoded variant van remco/EpicNext-Cms) +ADD https://gitlab.epicnabbo.nl/api/v4/projects/remco%2FEpicNext-Cms/repository/branches/main /tmp/commit.json +RUN git pull origin main + # Build the production bundle. # The .env file is loaded ONLY inside this RUN layer (not persisted as ENV, so no # secrets end up in the image) — Next.js needs NEXT_PUBLIC_* + validated build-time @@ -69,7 +76,18 @@ RUN --mount=type=cache,id=epicnext-next,target=/app/.next/cache,sharing=locked \ FROM node:26.8.1-bookworm-slim AS runner # Catalog Studio publishes to self-hosted Git repositories over HTTPS. -RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates && rm -rf /var/lib/apt/lists/* +# curl is the fallback downloader for clone sources that block Node's TLS +# fingerprint (e.g. Leet.city) — see import/core/curl-fetch.ts. +# curl-impersonate ships a curl built with Chrome's exact TLS fingerprint +# (statically-linked BoringSSL; only libz/libc required). Several sources +# enable Cloudflare `cf-mitigated: challenge` against the distro curl's JA3, +# so prefer the impersonated binary at runtime (curl-fetch.ts resolves it). +RUN apt-get update && apt-get install -y --no-install-recommends git curl ca-certificates \ + && mkdir -p /opt/curl-impersonate \ + && curl -fsSL "https://github.com/lwthiker/curl-impersonate/releases/download/v0.6.1/curl-impersonate-v0.6.1.x86_64-linux-gnu.tar.gz" \ + | tar -xz -C /opt/curl-impersonate \ + && chmod +x /opt/curl-impersonate/curl_chrome116 /opt/curl-impersonate/curl-impersonate-chrome \ + && rm -rf /var/lib/apt/lists/* # The CMS writes to bind-mounted host directories (/var/www/Gamedata is owned by # the host's www-data user, UID/GID 33). The node base image already ships a @@ -107,4 +125,4 @@ VOLUME ["/app/public/nitro-assets", "/app/public/swf", "/app/storage"] USER ${RUN_USER} -CMD ["node", "server.js"] +CMD ["node", "server.js"] \ No newline at end of file diff --git a/backups/legacy-delete/items_base_2026-09-06T14-59-53-180Z.json b/backups/legacy-delete/items_base_2026-09-06T14-59-53-180Z.json new file mode 100644 index 00000000..f6ddea20 --- /dev/null +++ b/backups/legacy-delete/items_base_2026-09-06T14-59-53-180Z.json @@ -0,0 +1,1684 @@ +[ + { + "id": 20060, + "sprite_id": 20060, + "item_name": "DEAL_HC_1", + "public_name": "DEAL_HC_1", + "type": "h", + "width": 0, + "length": 0, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "683", + "rare": "0" + }, + { + "id": 20100, + "sprite_id": 20100, + "item_name": "Poker", + "public_name": "", + "type": "i", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "429", + "rare": "0" + }, + { + "id": 73671, + "sprite_id": 73671, + "item_name": "fun tree", + "public_name": "0", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 73684, + "sprite_id": 73684, + "item_name": "_pie", + "public_name": "0", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 190880, + "sprite_id": 190880, + "item_name": "diagon_DoorHp_name", + "public_name": "diagon_DoorHp_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 509402, + "sprite_id": 509402, + "item_name": "wf_cnd_habbo_owns_badge", + "public_name": "0", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 1, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 2926422, + "sprite_id": 2926422, + "item_name": "dicebronze_name", + "public_name": "dicebronze_name", + "type": "S", + "width": 1, + "length": 1, + "stack_height": 0.7, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "dice", + "interaction_modes_count": 6, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 2926423, + "sprite_id": 2926423, + "item_name": "dicecream_name", + "public_name": "dicecream_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0.7, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "dice", + "interaction_modes_count": 6, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 2926424, + "sprite_id": 2926424, + "item_name": "dicegold_name", + "public_name": "dicegold_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0.7, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "dice", + "interaction_modes_count": 6, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 2926425, + "sprite_id": 2926425, + "item_name": "dicesilver_name", + "public_name": "dicesilver_name", + "type": "S", + "width": 1, + "length": 1, + "stack_height": 0.7, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "dice", + "interaction_modes_count": 6, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 13783863, + "sprite_id": 13783863, + "item_name": "habbox_finley_soutien_gorge", + "public_name": "habbox_finley_soutien_gorge_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 13783864, + "sprite_id": 13783864, + "item_name": "habbox_finley_vetements1", + "public_name": "habbox_finley_vetements1_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 13783865, + "sprite_id": 13783865, + "item_name": "habbox_finley_vetements2", + "public_name": "habbox_finley_vetements2_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 13783866, + "sprite_id": 13783866, + "item_name": "habbox_finley_vetements3", + "public_name": "habbox_finley_vetements3_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 13783867, + "sprite_id": 13783867, + "item_name": "habbox_finley_vetements4", + "public_name": "habbox_finley_vetements4_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 13783868, + "sprite_id": 13783868, + "item_name": "habbox_finley_vet_chapeau", + "public_name": "habbox_finley_vet_chapeau_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 22834655, + "sprite_id": 22834655, + "item_name": "icon_39154.png", + "public_name": "icon_39154.png_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 29109645, + "sprite_id": 29109645, + "item_name": "hp_bottle_name", + "public_name": "hp_bottle_name", + "type": "1", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "1299", + "rare": "0" + }, + { + "id": 32610975, + "sprite_id": 32610975, + "item_name": "icon_59613.png", + "public_name": "icon_59613.png_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 36636959, + "sprite_id": 36636959, + "item_name": "dicerosegold_name", + "public_name": "dicerosegold_name", + "type": "S", + "width": 1, + "length": 1, + "stack_height": 0.5, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "dice", + "interaction_modes_count": 6, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 36885449, + "sprite_id": 36885449, + "item_name": "patty3_name", + "public_name": "patty3_name", + "type": "1", + "width": 9, + "length": 5, + "stack_height": 0, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "6677", + "rare": "0" + }, + { + "id": 46888098, + "sprite_id": 46888098, + "item_name": "abbot_hp_door_name", + "public_name": "abbot_hp_door_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888099, + "sprite_id": 46888099, + "item_name": "ariana_doorHp_name", + "public_name": "ariana_doorHp_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888100, + "sprite_id": 46888100, + "item_name": "borisWalldoor_name", + "public_name": "borisWalldoor_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888116, + "sprite_id": 46888116, + "item_name": "edwardDoor_hp_name", + "public_name": "edwardDoor_hp_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888117, + "sprite_id": 46888117, + "item_name": "elizabethdoor_name", + "public_name": "elizabethdoor_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888118, + "sprite_id": 46888118, + "item_name": "fronsacDoorHp_name", + "public_name": "fronsacDoorHp_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888119, + "sprite_id": 46888119, + "item_name": "georgeDoor_hp_name", + "public_name": "georgeDoor_hp_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888120, + "sprite_id": 46888120, + "item_name": "google_doorHp_name", + "public_name": "google_doorHp_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888121, + "sprite_id": 46888121, + "item_name": "kitchens_door_name", + "public_name": "kitchens_door_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888122, + "sprite_id": 46888122, + "item_name": "percivalPratt_name", + "public_name": "percivalPratt_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888123, + "sprite_id": 46888123, + "item_name": "quadro_golosa_name", + "public_name": "quadro_golosa_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888124, + "sprite_id": 46888124, + "item_name": "ravenclawDoor_name", + "public_name": "ravenclawDoor_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888125, + "sprite_id": 46888125, + "item_name": "signoraGrassa_name", + "public_name": "signoraGrassa_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888126, + "sprite_id": 46888126, + "item_name": "termeritus_hp_name", + "public_name": "termeritus_hp_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 46888127, + "sprite_id": 46888127, + "item_name": "timotyWl_door_name", + "public_name": "timotyWl_door_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 71203799, + "sprite_id": 71203799, + "item_name": "ptrnblok1_name", + "public_name": "ptrnblok1_name", + "type": "1", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "2255", + "rare": "0" + }, + { + "id": 73543675, + "sprite_id": 73543675, + "item_name": "icon_14511.png", + "public_name": "icon_14511.png_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 76961373, + "sprite_id": 76961373, + "item_name": "Unconfirmed 766997.crdownload", + "public_name": "Unconfirmed 766997.crdownload_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 85323276, + "sprite_id": 85323276, + "item_name": "blackBoard_hp_name", + "public_name": "blackBoard_hp_name", + "type": "2", + "width": 1, + "length": 3, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 85323279, + "sprite_id": 85323279, + "item_name": "BwcastleDoor2_name", + "public_name": "BwcastleDoor2_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 85323280, + "sprite_id": 85323280, + "item_name": "BW_Arrow_name", + "public_name": "BW_Arrow_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 85323281, + "sprite_id": 85323281, + "item_name": "BW_castleDoor_name", + "public_name": "BW_castleDoor_name", + "type": "1", + "width": 1, + "length": 0, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 85323293, + "sprite_id": 85323293, + "item_name": "calderoniHP_name", + "public_name": "calderoniHP_name", + "type": "1", + "width": 1, + "length": 2, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 89266385, + "sprite_id": 89266385, + "item_name": "uni_drag", + "public_name": "0", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 89311602, + "sprite_id": 89311602, + "item_name": "icon_3850.png", + "public_name": "icon_3850.png_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 92256888, + "sprite_id": 92256888, + "item_name": "habbox_skold_cuisine_tasse", + "public_name": "habbox_skold_cuisine_tasse_name", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 99910864, + "sprite_id": 99910864, + "item_name": "cama_rosa1_name", + "public_name": "cama_rosa1_name", + "type": "2", + "width": 4, + "length": 2, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "bed", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "2233", + "rare": "0" + }, + { + "id": 99910865, + "sprite_id": 99910865, + "item_name": "cama_verde1_name", + "public_name": "cama_verde1_name", + "type": "2", + "width": 3, + "length": 2, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "bed", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "2233", + "rare": "0" + }, + { + "id": 99910866, + "sprite_id": 99910866, + "item_name": "cama_verde2_name", + "public_name": "cama_verde2_name", + "type": "1", + "width": 3, + "length": 1, + "stack_height": 1, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "bed", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "2233", + "rare": "0" + }, + { + "id": 132663117, + "sprite_id": 132663117, + "item_name": "0", + "public_name": "0", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 996661664, + "sprite_id": 996661664, + "item_name": "fruitmachin2", + "public_name": "fruitmachin2", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 1996663036, + "sprite_id": 1996663036, + "item_name": "IcedOutBrache_by_CallMe", + "public_name": "Rare Brache", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 1996663040, + "sprite_id": 1996663040, + "item_name": "cake", + "public_name": "Rare Cake", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 2000025635, + "sprite_id": 2000025635, + "item_name": "At Chair Jakeyel", + "public_name": "0", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "chair", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 2000025636, + "sprite_id": 2000025636, + "item_name": "At Pjake", + "public_name": "0", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 2000025637, + "sprite_id": 2000025637, + "item_name": "At Table Jakeyel Bigsq", + "public_name": "0", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "table", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + }, + { + "id": 2000025638, + "sprite_id": 2000025638, + "item_name": "At Tv Finnhairfl", + "public_name": "0", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "0", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": null, + "rare": "0" + } +] diff --git a/backups/legacy-delete/items_base_second-batch_2026-09-06T15-01-00-161Z.json b/backups/legacy-delete/items_base_second-batch_2026-09-06T15-01-00-161Z.json new file mode 100644 index 00000000..5b11b69c --- /dev/null +++ b/backups/legacy-delete/items_base_second-batch_2026-09-06T15-01-00-161Z.json @@ -0,0 +1,205 @@ +[ + { + "id": 132, + "sprite_id": 132, + "item_name": "floortile", + "public_name": "Floor Tile", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 1, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "429", + "rare": "0" + }, + { + "id": 420, + "sprite_id": 420, + "item_name": "soft_jaggara_norja", + "public_name": "Norja-pehmojakkara", + "type": "s", + "width": 1, + "length": 3, + "stack_height": 1.7, + "allow_stack": 1, + "allow_sit": 1, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 1, + "vending_ids": "0", + "multiheight": "", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "429", + "rare": "0" + }, + { + "id": 1001, + "sprite_id": 1001, + "item_name": "Chess", + "public_name": "", + "type": "i", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "429", + "rare": "0" + }, + { + "id": 1011, + "sprite_id": 1011, + "item_name": "TicTacToe", + "public_name": "", + "type": "i", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "429", + "rare": "0" + }, + { + "id": 1021, + "sprite_id": 1021, + "item_name": "BattleShip", + "public_name": "", + "type": "i", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "429", + "rare": "0" + }, + { + "id": 1659, + "sprite_id": 1659, + "item_name": "ticket", + "public_name": "Big Ticket Bundle", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 1, + "allow_stack": 1, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "429", + "rare": "0" + }, + { + "id": 10056, + "sprite_id": 10056, + "item_name": "Vacuum", + "public_name": "laundry_r18_vacuum", + "type": "s", + "width": 1, + "length": 1, + "stack_height": 0, + "allow_stack": 0, + "allow_sit": 0, + "allow_lay": 0, + "allow_walk": 0, + "allow_gift": 1, + "allow_trade": 1, + "allow_recycle": 0, + "allow_marketplace_sell": 0, + "allow_inventory_stack": 1, + "interaction_type": "default", + "interaction_modes_count": 0, + "vending_ids": "0", + "multiheight": "", + "customparams": "", + "effect_id_male": 0, + "effect_id_female": 0, + "clothing_on_walk": "", + "page_id": "9966", + "rare": "0" + } +] diff --git a/docker-compose.yml b/docker-compose.yml index fc560f1e..0b0fbe6d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,6 +48,27 @@ services: start_period: 40s mem_limit: 2g + # ── FlareSolverr (Cloudflare bypass for clone sources) ── + # Solves Cloudflare/TLS-fingerprint blocks via a headless Chrome browser. + # The CMS calls this on http://localhost:8191 for sources that block Node's + # TLS fingerprint (e.g. Leet.city). Used by src/lib/services/flare-solver.ts. + flaresolverr: + image: ghcr.io/flaresolverr/flaresolverr:latest + container_name: flaresolverr + network_mode: host + restart: unless-stopped + environment: + - LOG_LEVEL=info + - BROWSER_TIMEOUT=60000 + - BROWSER_WORKERS=1 + mem_limit: 2g + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:8191/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + # ── Opt-in: Octane-Renderer (Habbo avatar imager) ── # Serves /imaging on port 3030 (the CMS proxies /imaging to it). Renders # avatars into /var/www/Gamedata/habbo-imaging, so it needs RW access. diff --git a/src/lib/services/catalog-audit.ts b/src/lib/services/catalog-audit.ts index 0ec9922e..cb4aae9c 100644 --- a/src/lib/services/catalog-audit.ts +++ b/src/lib/services/catalog-audit.ts @@ -50,6 +50,12 @@ export interface AuditEvent { missingFromSources?: MissingFromSource[]; unrepairable?: UnrepairableItem[]; summary?: AuditSummary; + furniDataIdConflicts?: Array<{ + classname: string; + itemId: number; + conflictingId: number; + existingClassname: string; + }>; repair?: RepairStats; repairNitro?: RepairStats; sql?: string; diff --git a/src/lib/services/flare-solver.ts b/src/lib/services/flare-solver.ts index e55e77d3..7f011879 100644 --- a/src/lib/services/flare-solver.ts +++ b/src/lib/services/flare-solver.ts @@ -3,10 +3,28 @@ const FLARESOLVERR_URL = const FLARESOLVERR_API = `${FLARESOLVERR_URL}/v1`; -export async function fetchWithFlareSolver( +type FlareCookie = { + name: string; + value: string; + domain: string; + path: string; +}; + +type FlareSolution = { + response: string; + status: number; + cookies: FlareCookie[]; +}; + +type FlareResult = { + solution?: FlareSolution; + error?: string; +}; + +async function flareRequest( url: string, timeout = 30000, -): Promise { +): Promise { const res = await fetch(FLARESOLVERR_API, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -24,10 +42,7 @@ export async function fetchWithFlareSolver( ); } - const data = (await res.json()) as { - solution?: { response: string }; - error?: string; - }; + const data = (await res.json()) as FlareResult; if (data.error) { throw new Error(`FlareSolverr error: ${data.error}`); @@ -37,5 +52,31 @@ export async function fetchWithFlareSolver( throw new Error("FlareSolverr: no solution in response"); } - return data.solution.response; + return data.solution; +} + +export async function fetchWithFlareSolver( + url: string, + timeout = 30000, +): Promise { + const solution = await flareRequest(url, timeout); + return solution.response; +} + +/** + * Solve a Cloudflare challenge for the given URL and return the clearance + * cookies as a `-b "name=value; ..."` argument string suitable for curl. + * Returns null if FlareSolverr is unavailable or no cookies were set. + */ +export async function getFlareSolverrCookies( + url: string, + timeout = 30000, +): Promise { + try { + const solution = await flareRequest(url, timeout); + if (!solution.cookies?.length) return null; + return solution.cookies.map((c) => `${c.name}=${c.value}`).join("; "); + } catch { + return null; + } } diff --git a/src/lib/services/import/core/curl-fetch.ts b/src/lib/services/import/core/curl-fetch.ts index 1f0cac82..6bdd181b 100644 --- a/src/lib/services/import/core/curl-fetch.ts +++ b/src/lib/services/import/core/curl-fetch.ts @@ -1,5 +1,5 @@ import { execFile } from "node:child_process"; -import { promises as fs } from "node:fs"; +import { existsSync, promises as fs } from "node:fs"; import { promisify } from "node:util"; const execFileAsync = promisify(execFile); @@ -8,8 +8,17 @@ const execFileAsync = promisify(execFile); * Some retro CDNs (e.g. Leet.city) block Node's fetch by TLS fingerprint * (JA3/JA4) even though the same request succeeds from a real browser or * curl. This helper shells out to `curl` as a fallback for those hosts. + * + * The runtime image ships `curl-impersonate` (a curl compiled with Chrome's + * exact TLS fingerprint, statically-bundled BoringSSL) at /opt/curl-impersonate + * because several sources enable Cloudflare `cf-mitigated: challenge` against + * the distro curl's fingerprint too. Prefer it when present, else fall back to + * the system curl. */ +const IMPERSONATE_CURL = "/opt/curl-impersonate/curl_chrome116"; +const CURL_BIN = existsSync(IMPERSONATE_CURL) ? IMPERSONATE_CURL : "curl"; + const CURL_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"; @@ -37,7 +46,7 @@ export async function curlFetchText( url: string, timeoutMs = 30_000, ): Promise { - const { stdout } = await execFileAsync("curl", curlArgs(url, []), { + const { stdout } = await execFileAsync(CURL_BIN, curlArgs(url, []), { timeout: timeoutMs, maxBuffer: 512 * 1024 * 1024, }); @@ -49,10 +58,12 @@ export async function curlDownload( url: string, destPath: string, timeoutMs = 30_000, + cookieHeader?: string, ): Promise<{ ok: boolean; size: number }> { try { + const cookieArgs = cookieHeader ? ["-H", `Cookie: ${cookieHeader}`] : []; await execFileAsync( - "curl", + CURL_BIN, [ "-sSL", "--compressed", @@ -67,6 +78,7 @@ export async function curlDownload( "Accept: application/octet-stream,*/*;q=0.9", "-H", "Accept-Language: en-US,en;q=0.9", + ...cookieArgs, "-o", destPath, url, diff --git a/src/lib/services/import/core/download.ts b/src/lib/services/import/core/download.ts index c6f067f4..8286711d 100644 --- a/src/lib/services/import/core/download.ts +++ b/src/lib/services/import/core/download.ts @@ -1,7 +1,8 @@ import { promises as fs } from "node:fs"; +import { getFlareSolverrCookies } from "@/lib/services/flare-solver"; import { parseNitroBundle } from "../../swf/nitro-builder"; - import { browserHeaders } from "./browser-headers"; +import { curlDownload } from "./curl-fetch"; export function validateSwfBytes(buffer: Buffer): boolean { if (buffer.length < 8) return false; @@ -58,6 +59,38 @@ export async function downloadFile( const maxRetries = options?.maxRetries ?? 3; const baseDelay = 1000; + const curlFallback = async (): Promise => { + let curlRes = await curlDownload(url, destPath); + if (!curlRes.ok) { + const cookieHeader = await getFlareSolverrCookies(url); + curlRes = await curlDownload( + url, + destPath, + 30_000, + cookieHeader ?? undefined, + ); + } + if (!curlRes.ok) return false; + const validate = options?.validate; + if (validate) { + let valid = false; + try { + const buffer = await fs.readFile(destPath); + valid = + (validate === "swf" && validateSwfBytes(buffer)) || + (validate === "png" && validatePngBytes(buffer)) || + (validate === "nitro" && validateNitroBytes(buffer)); + } catch { + valid = false; + } + if (!valid) { + await fs.unlink(destPath).catch(() => {}); + return false; + } + } + return true; + }; + for (let attempt = 0; attempt <= maxRetries; attempt++) { let stage: "download" | "write" = "download"; try { @@ -74,6 +107,13 @@ export async function downloadFile( const deterministic = res.status === 404 || res.status === 410 || res.status === 403; if (deterministic || attempt === maxRetries) { + // HTTP 403 is often a TLS-fingerprint / Cloudflare challenge + // block (e.g. Leet.city) — retry via curl, with FlareSolverr + // clearance cookies if the plain curl is also challenged. + if (res.status === 403 && (await curlFallback())) { + const stat = await fs.stat(destPath); + return { ok: true, size: stat.size }; + } console.warn( `[import-download] Download failed ${url}: ${res.status}${deterministic ? " (deterministic, not retrying)" : ` after ${maxRetries + 1} attempts`}`, ); @@ -108,6 +148,12 @@ export async function downloadFile( return { ok: true, size: buffer.length }; } catch (err) { if (attempt === maxRetries || stage === "write") { + // TLS fingerprint aborts land here too — try curl/FlareSolverr + // before reporting the network failure. + if (stage === "download" && (await curlFallback())) { + const stat = await fs.stat(destPath); + return { ok: true, size: stat.size }; + } console.warn( `[import-download] Download error ${url}:`, (err as Error).message,