From 14660586c48acc0e57af2a8eccfb632a239956f8 Mon Sep 17 00:00:00 2001 From: aramesh-site deploy Date: Mon, 26 Oct 2026 12:28:28 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20=D9=87=D9=85=DA=AF=D8=A7=D9=85?= =?UTF-8?q?=E2=80=8C=D8=B3=D8=A7=D8=B2=DB=8C=20=D9=88=D8=B6=D8=B9=DB=8C?= =?UTF-8?q?=D8=AA=20=D8=B2=D9=86=D8=AF=D9=87=D9=94=20=D8=B3=D8=B1=D9=88?= =?UTF-8?q?=D8=B1=20=D8=A8=D8=A7=20=D9=85=D8=AE=D8=B2=D9=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit شامل کارهای چند نشست موازی که تا این‌جا فقط روی سرور بود: - نقشهٔ دفتر مرکزی در فوتر (LazyOfficeMap) + MapViewport/LazyMaps/mapProjects - صفحهٔ مدیریت مقالات (AdminArticlesPage) + میان‌بر ویرایش پیش‌نویس در وبلاگ - مهاجرت ۰۰۰۴: updated_at مقالات/پروژه‌ها - chatbot/Dockerfile: نصب دومرحله‌ای torch (کاربردی برای PyPI فیلترشده) - seed: خالی‌کردن آمارهای دموی story - docker-compose: اصلاح پسوند @file میان‌افزارهای Traefik - حذف فایل موقت frontend/.tmp-style.json و اصلاحات جزئی UI با این کامیت، آن‌چه روی سرور اجرا می‌شود Exactly همان چیزی است که در مخزن ثبت شده. --- backend/app/seed_data.py | 11 +- backend/app/seed_data_en.py | 19 +- .../0004_article_project_updated_at.py | 39 + chatbot/Dockerfile | 10 +- docker-compose.prod.yml | 8 +- frontend/.tmp-style.json | 2065 ----------------- frontend/Caddyfile | 22 +- frontend/index.html | 17 +- frontend/src/App.tsx | 2 + frontend/src/admin/AdminArticlesPage.tsx | 136 ++ frontend/src/admin/AdminBar.tsx | 22 +- frontend/src/admin/ProjectWritePage.tsx | 1 + frontend/src/components/Footer.tsx | 9 + frontend/src/components/admin/ImageField.tsx | 9 +- frontend/src/components/home/Story.tsx | 43 +- frontend/src/data/mapProjects.ts | 7 + frontend/src/i18n/dict.ts | 4 + frontend/src/map/LazyMaps.tsx | 43 +- frontend/src/map/MapViewport.tsx | 57 +- frontend/src/map/OfficeMap.tsx | 63 + frontend/src/pages/About.tsx | 3 +- frontend/src/pages/Blog.tsx | 32 +- frontend/src/pages/Home.tsx | 3 +- 23 files changed, 488 insertions(+), 2137 deletions(-) create mode 100644 backend/migrations/versions/0004_article_project_updated_at.py delete mode 100644 frontend/.tmp-style.json create mode 100644 frontend/src/admin/AdminArticlesPage.tsx create mode 100644 frontend/src/map/OfficeMap.tsx diff --git a/backend/app/seed_data.py b/backend/app/seed_data.py index 17a9c77..b793660 100644 --- a/backend/app/seed_data.py +++ b/backend/app/seed_data.py @@ -322,10 +322,13 @@ STORY = { ], "quote": "«ساختمان برای ما فقط آجر و بتن نیست؛ بلکه تعهدی است به کیفیت زندگی ساکنان. ما فضاهایی می‌سازیم که در گذر زمان ارزش و هویت خود را حفظ کنند.»", "quote_author": "کامران آرامش — مدیرعامل شرکت ساختمانی آرامش", - "stat1_value": "۹۴٪", - "stat1_label": "تحویل به‌موقع پروژه‌ها", - "stat2_value": "۵ سال", - "stat2_label": "پشتیبانی پس از تحویل", + # آمارهای دموی «۹۴٪ تحویل به‌موقع» و «۵ سال پشتیبانی» خالی شدند — ادعای + # بدون پشتوانه بودند و در سایت واقعی هم پاک شده‌اند؛ این‌جا هم خالی می‌مانند + # تا اگر روزی جدول story خالی شود، seed آن‌ها را برنگرداند + "stat1_value": "", + "stat1_label": "", + "stat2_value": "", + "stat2_label": "", "link_label": "داستان کامل ما", "link_href": "/about", "bento": [ diff --git a/backend/app/seed_data_en.py b/backend/app/seed_data_en.py index b89e418..773bf66 100644 --- a/backend/app/seed_data_en.py +++ b/backend/app/seed_data_en.py @@ -396,14 +396,17 @@ STORY = { "quote_en": "\u201cA building, for us, is not just brick and concrete; it is a commitment to the quality of life of its residents. We build spaces that keep their value and identity as time passes.\u201d", "quote_author": "کامران آرامش — مدیرعامل شرکت ساختمانی آرامش", "quote_author_en": "Kamran Aramesh — CEO, Aramesh Construction Group", - "stat1_value": "۹۴٪", - "stat1_value_en": "94%", - "stat1_label": "تحویل به‌موقع پروژه‌ها", - "stat1_label_en": "On-time project delivery", - "stat2_value": "۵ سال", - "stat2_value_en": "5 years", - "stat2_label": "پشتیبانی پس از تحویل", - "stat2_label_en": "Post-handover support", + # Demo stats «94% on-time» / «5 years support» emptied — unsupported claims, + # removed from the live site too; kept empty here so a future reseed of an + # empty story table cannot bring them back + "stat1_value": "", + "stat1_value_en": "", + "stat1_label": "", + "stat1_label_en": "", + "stat2_value": "", + "stat2_value_en": "", + "stat2_label": "", + "stat2_label_en": "", "link_label": "داستان کامل ما", "link_label_en": "Our full story", "bento": [], diff --git a/backend/migrations/versions/0004_article_project_updated_at.py b/backend/migrations/versions/0004_article_project_updated_at.py new file mode 100644 index 0000000..13bb48e --- /dev/null +++ b/backend/migrations/versions/0004_article_project_updated_at.py @@ -0,0 +1,39 @@ +"""زمان آخرین به‌روزرسانی مقاله‌ها و پروژه‌ها — ستون updated_at برای lastmod نقشه سایت. + +نقشه سایت lastmod معتبر لازم دارد که آخرین تغییر معنادار محتوا را نشان دهد؛ +تاریخ نمایشی مقاله‌ها (شمسی دست‌نویس) برای این کار مناسب نیست. ردیف‌های قدیمی +NULL می‌مانند و lastmod فقط برای ردیف‌هایی صادر می‌شود که بعداً در پنل ذخیره شده‌اند — +تاریخ جعلی همگن (همه = امروز) به‌عنوان سیگنال منفی شناخته می‌شود. + +Revision ID: 0004 +Revises: 0003 +Create Date: 2026-10-11 +""" +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +revision: str = "0004" +down_revision: Union[str, None] = "0003" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + +_TABLES = ("articles", "projects") + + +def upgrade() -> None: + for table in _TABLES: + cols = {c["name"] for c in sa.inspect(op.get_bind()).get_columns(table)} + if "updated_at" not in cols: + op.add_column( + table, + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True), + ) + + +def downgrade() -> None: + for table in _TABLES: + cols = {c["name"] for c in sa.inspect(op.get_bind()).get_columns(table)} + if "updated_at" in cols: + op.drop_column(table, "updated_at") diff --git a/chatbot/Dockerfile b/chatbot/Dockerfile index a3aee6c..bbf73a1 100644 --- a/chatbot/Dockerfile +++ b/chatbot/Dockerfile @@ -10,8 +10,14 @@ COPY requirements.txt /srv/chatbot/requirements.txt # (ایمیج ~۹.۷GB روی سرور بدون GPU). نسخهٔ CPU حدود ۲۰۰MB است — همین اول نصب # می‌شود تا pip در گام بعد آن را برآورده‌شده ببیند. # روی ماشینی با GPU می‌توان این خط را حذف کرد. -RUN pip install --no-cache-dir --index-url https://download.pytorch.org/whl/cpu "torch>=2.2" -RUN pip install --no-cache-dir -r /srv/chatbot/requirements.txt +# دو مرحله‌ای (PyPI از این سرور فیلتر است): (۱) خودِ torch فقط از ایندکس CPU +# پی‌تورچ با --no-deps — چون وابستگی‌های torch در آن ایندکس به +# files.pythonhosted.org لینک می‌خورند؛ (۲) وابستگی‌های torch از میرور در +# دسترس — torch از قبل نصب است و pip فقط وابستگی‌های missing را می‌گیرد. +ARG PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple +RUN pip install --no-cache-dir --index-url https://download.pytorch.org/whl/cpu --no-deps "torch>=2.2" \ + && pip install --no-cache-dir --index-url "${PIP_INDEX_URL}" "torch>=2.2" +RUN pip install --no-cache-dir --index-url "${PIP_INDEX_URL}" -r /srv/chatbot/requirements.txt COPY . /srv/chatbot/ diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index cf844d4..061d4c4 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -53,7 +53,7 @@ services: - traefik.http.routers.media.entrypoints=websecure - traefik.http.routers.media.tls.certresolver=le - traefik.http.routers.media.priority=100 - - traefik.http.routers.media.middlewares=${PREVIEW_MIDDLEWARES:+$PREVIEW_MIDDLEWARES,}media-rewrite,sec-headers + - traefik.http.routers.media.middlewares=${PREVIEW_MIDDLEWARES:+$PREVIEW_MIDDLEWARES,}media-rewrite,sec-headers@file - traefik.http.middlewares.media-rewrite.replacepathregex.regex=^/media/(.*) - traefik.http.middlewares.media-rewrite.replacepathregex.replacement=/aramesh-media/$$1 - traefik.http.services.media.loadbalancer.server.port=9000 @@ -70,13 +70,13 @@ services: - traefik.http.routers.api.rule=Host(`${DOMAIN}`) && PathPrefix(`/api`) - traefik.http.routers.api.entrypoints=websecure - traefik.http.routers.api.tls.certresolver=le - - traefik.http.routers.api.middlewares=${PREVIEW_MIDDLEWARES:+$PREVIEW_MIDDLEWARES,}api-flood,sec-headers + - traefik.http.routers.api.middlewares=${PREVIEW_MIDDLEWARES:+$PREVIEW_MIDDLEWARES,}api-flood@file,sec-headers@file # سئو: همهٔ صفحات عمومی از بک‌اند سرو می‌شوند تا متا در HTML اولیه تزریق شود — # مسیرهای استاتیک دقیق‌اند (Path) تا assets/ فونت‌ها از Caddy سرو شوند - traefik.http.routers.seo.rule=Host(`${DOMAIN}`) && (Path(`/`) || Path(`/about`) || Path(`/services`) || Path(`/investment`) || Path(`/faq`) || Path(`/contact`) || PathPrefix(`/blog/`) || Path(`/blog`) || PathPrefix(`/projects/`) || Path(`/projects`) || Path(`/en`) || PathPrefix(`/en/`) || PathPrefix(`/robots.txt`) || PathPrefix(`/sitemap.xml`) || PathPrefix(`/llms.txt`) || Path(`/.well-known/indexnow-key.txt`) || Path(`/daa4488a8e2905312b5efd02ad5fc614.txt`) || PathRegexp(`^/[a-f0-9]{32}\.txt$`) || Path(`/projects-images.json`) || PathRegexp(`^/projects/.+/images\.zip$`)) - traefik.http.routers.seo.entrypoints=websecure - traefik.http.routers.seo.tls.certresolver=le - - traefik.http.routers.seo.middlewares=${PREVIEW_MIDDLEWARES:+$PREVIEW_MIDDLEWARES,}sec-headers + - traefik.http.routers.seo.middlewares=${PREVIEW_MIDDLEWARES:+$PREVIEW_MIDDLEWARES,}sec-headers@file frontend: labels: @@ -114,7 +114,7 @@ services: - traefik.http.routers.chat.entrypoints=websecure - traefik.http.routers.chat.tls.certresolver=le - traefik.http.routers.chat.priority=100 - - traefik.http.routers.chat.middlewares=${PREVIEW_MIDDLEWARES:+$PREVIEW_MIDDLEWARES,}api-flood,sec-headers + - traefik.http.routers.chat.middlewares=${PREVIEW_MIDDLEWARES:+$PREVIEW_MIDDLEWARES,}api-flood@file,sec-headers@file umami: labels: diff --git a/frontend/.tmp-style.json b/frontend/.tmp-style.json deleted file mode 100644 index 8dda233..0000000 --- a/frontend/.tmp-style.json +++ /dev/null @@ -1,2065 +0,0 @@ -{ - "version": 8, - "name": "Dark Matter", - "metadata": { - "mapbox:autocomposite": false, - "mapbox:groups": { - "101da9f13b64a08fa4b6ac1168e89e5f": { - "collapsed": false, - "name": "Places" - }, - "a14c9607bc7954ba1df7205bf660433f": { - "name": "Boundaries" - }, - "b6371a3f2f5a9932464fa3867530a2e5": { - "collapsed": false, - "name": "Transportation" - } - }, - "mapbox:type": "template", - "openmaptiles:mapbox:owner": "openmaptiles", - "openmaptiles:mapbox:source:url": "mapbox://openmaptiles.4qljc88t", - "openmaptiles:version": "3.x" - }, - "sources": { - "openmaptiles": { - "type": "vector", - "url": "mbtiles://{v3}" - } - }, - "sprite": "{styleJsonFolder}/sprite", - "glyphs": "{fontstack}/{range}.pbf", - "layers": [ - { - "id": "background", - "type": "background", - "paint": { - "background-color": "rgb(12,12,12)" - } - }, - { - "id": "water", - "type": "fill", - "source": "openmaptiles", - "source-layer": "water", - "filter": [ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "!=", - "brunnel", - "tunnel" - ] - ], - "layout": { - "visibility": "visible" - }, - "paint": { - "fill-antialias": false, - "fill-color": "rgb(27 ,27 ,29)" - } - }, - { - "id": "landcover_ice_shelf", - "type": "fill", - "source": "openmaptiles", - "source-layer": "landcover", - "maxzoom": 8, - "filter": [ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "==", - "subclass", - "ice_shelf" - ] - ], - "layout": { - "visibility": "visible" - }, - "paint": { - "fill-color": "rgb(12,12,12)", - "fill-opacity": 0.7 - } - }, - { - "id": "landcover_glacier", - "type": "fill", - "source": "openmaptiles", - "source-layer": "landcover", - "maxzoom": 8, - "filter": [ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "==", - "subclass", - "glacier" - ] - ], - "layout": { - "visibility": "visible" - }, - "paint": { - "fill-color": "hsl(0, 1%, 2%)", - "fill-opacity": { - "base": 1, - "stops": [ - [ - 0, - 1 - ], - [ - 8, - 0.5 - ] - ] - } - } - }, - { - "id": "landuse_residential", - "type": "fill", - "source": "openmaptiles", - "source-layer": "landuse", - "maxzoom": 9, - "filter": [ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "==", - "class", - "residential" - ] - ], - "layout": { - "visibility": "visible" - }, - "paint": { - "fill-color": "hsl(0, 2%, 5%)", - "fill-opacity": 0.4 - } - }, - { - "id": "landcover_wood", - "type": "fill", - "source": "openmaptiles", - "source-layer": "landcover", - "minzoom": 10, - "filter": [ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "==", - "class", - "wood" - ] - ], - "layout": { - "visibility": "visible" - }, - "paint": { - "fill-color": "rgb(32,32,32)", - "fill-opacity": { - "base": 0.3, - "stops": [ - [ - 8, - 0 - ], - [ - 10, - 0.8 - ], - [ - 13, - 0.4 - ] - ] - }, - "fill-pattern": "wood-pattern", - "fill-translate": [ - 0, - 0 - ] - } - }, - { - "id": "landuse_park", - "type": "fill", - "source": "openmaptiles", - "source-layer": "landuse", - "filter": [ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "==", - "class", - "park" - ] - ], - "layout": { - "visibility": "visible" - }, - "paint": { - "fill-color": "rgb(32,32,32)" - } - }, - { - "id": "waterway", - "type": "line", - "source": "openmaptiles", - "source-layer": "waterway", - "filter": [ - "==", - "$type", - "LineString" - ], - "layout": { - "visibility": "visible" - }, - "paint": { - "line-color": "rgb(27 ,27 ,29)" - } - }, - { - "id": "water_name", - "type": "symbol", - "source": "openmaptiles", - "source-layer": "water_name", - "filter": [ - "==", - "$type", - "LineString" - ], - "layout": { - "symbol-placement": "line", - "symbol-spacing": 500, - "text-field": "{name:latin}\n{name:nonlatin}", - "text-font": [ - "Metropolis Medium Italic", - "Noto Sans Italic" - ], - "text-rotation-alignment": "map", - "text-size": 12 - }, - "paint": { - "text-color": "hsla(0, 0%, 0%, 0.7)", - "text-halo-color": "hsl(0, 0%, 27%)" - } - }, - { - "id": "building", - "type": "fill", - "source": "openmaptiles", - "source-layer": "building", - "minzoom": 12, - "filter": [ - "==", - "$type", - "Polygon" - ], - "paint": { - "fill-antialias": true, - "fill-color": "rgb(10,10,10)", - "fill-outline-color": "rgb(27 ,27 ,29)" - } - }, - { - "id": "aeroway-taxiway", - "type": "line", - "metadata": { - "mapbox:group": "1444849345966.4436" - }, - "source": "openmaptiles", - "source-layer": "aeroway", - "minzoom": 12, - "filter": [ - "all", - [ - "in", - "class", - "taxiway" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "#181818", - "line-opacity": 1, - "line-width": { - "base": 1.55, - "stops": [ - [ - 13, - 1.8 - ], - [ - 20, - 20 - ] - ] - } - } - }, - { - "id": "aeroway-runway-casing", - "type": "line", - "metadata": { - "mapbox:group": "1444849345966.4436" - }, - "source": "openmaptiles", - "source-layer": "aeroway", - "minzoom": 11, - "filter": [ - "all", - [ - "in", - "class", - "runway" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "rgba(60,60,60,0.8)", - "line-opacity": 1, - "line-width": { - "base": 1.5, - "stops": [ - [ - 11, - 5 - ], - [ - 17, - 55 - ] - ] - } - } - }, - { - "id": "aeroway-area", - "type": "fill", - "metadata": { - "mapbox:group": "1444849345966.4436" - }, - "source": "openmaptiles", - "source-layer": "aeroway", - "minzoom": 4, - "filter": [ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "in", - "class", - "runway", - "taxiway" - ] - ], - "layout": { - "visibility": "visible" - }, - "paint": { - "fill-color": "#000", - "fill-opacity": 1 - } - }, - { - "id": "aeroway-runway", - "type": "line", - "metadata": { - "mapbox:group": "1444849345966.4436" - }, - "source": "openmaptiles", - "source-layer": "aeroway", - "minzoom": 11, - "filter": [ - "all", - [ - "in", - "class", - "runway" - ], - [ - "==", - "$type", - "LineString" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "#000", - "line-opacity": 1, - "line-width": { - "base": 1.5, - "stops": [ - [ - 11, - 4 - ], - [ - 17, - 50 - ] - ] - } - } - }, - { - "id": "road_area_pier", - "type": "fill", - "metadata": {}, - "source": "openmaptiles", - "source-layer": "transportation", - "filter": [ - "all", - [ - "==", - "$type", - "Polygon" - ], - [ - "==", - "class", - "pier" - ] - ], - "layout": { - "visibility": "visible" - }, - "paint": { - "fill-antialias": true, - "fill-color": "rgb(12,12,12)" - } - }, - { - "id": "road_pier", - "type": "line", - "metadata": {}, - "source": "openmaptiles", - "source-layer": "transportation", - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "in", - "class", - "pier" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round" - }, - "paint": { - "line-color": "rgb(12,12,12)", - "line-width": { - "base": 1.2, - "stops": [ - [ - 15, - 1 - ], - [ - 17, - 4 - ] - ] - } - } - }, - { - "id": "highway_path", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "class", - "path" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "rgb(27 ,27 ,29)", - "line-dasharray": [ - 1.5, - 1.5 - ], - "line-opacity": 0.9, - "line-width": { - "base": 1.2, - "stops": [ - [ - 13, - 1 - ], - [ - 20, - 10 - ] - ] - } - } - }, - { - "id": "highway_minor", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 8, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "in", - "class", - "minor", - "service", - "track" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "#181818", - "line-opacity": 0.9, - "line-width": { - "base": 1.55, - "stops": [ - [ - 13, - 1.8 - ], - [ - 20, - 20 - ] - ] - } - } - }, - { - "id": "highway_major_casing", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 11, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "in", - "class", - "primary", - "secondary", - "tertiary", - "trunk" - ] - ], - "layout": { - "line-cap": "butt", - "line-join": "miter", - "visibility": "visible" - }, - "paint": { - "line-color": "rgba(60,60,60,0.8)", - "line-dasharray": [ - 12, - 0 - ], - "line-width": { - "base": 1.3, - "stops": [ - [ - 10, - 3 - ], - [ - 20, - 23 - ] - ] - } - } - }, - { - "id": "highway_major_inner", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 11, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "in", - "class", - "primary", - "secondary", - "tertiary", - "trunk" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "hsl(0, 0%, 7%)", - "line-width": { - "base": 1.3, - "stops": [ - [ - 10, - 2 - ], - [ - 20, - 20 - ] - ] - } - } - }, - { - "id": "highway_major_subtle", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 6, - "maxzoom": 11, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "in", - "class", - "primary", - "secondary", - "tertiary", - "trunk" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "#2a2a2a", - "line-width": { - "stops": [ - [ - 6, - 0 - ], - [ - 8, - 2 - ] - ] - } - } - }, - { - "id": "highway_motorway_casing", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 6, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "class", - "motorway" - ] - ], - "layout": { - "line-cap": "butt", - "line-join": "miter", - "visibility": "visible" - }, - "paint": { - "line-color": "rgba(60,60,60,0.8)", - "line-dasharray": [ - 2, - 0 - ], - "line-opacity": 1, - "line-width": { - "base": 1.4, - "stops": [ - [ - 5.8, - 0 - ], - [ - 6, - 3 - ], - [ - 20, - 40 - ] - ] - } - } - }, - { - "id": "highway_motorway_inner", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 6, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "class", - "motorway" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": { - "base": 1, - "stops": [ - [ - 5.8, - "hsla(0, 0%, 85%, 0.53)" - ], - [ - 6, - "#000" - ] - ] - }, - "line-width": { - "base": 1.4, - "stops": [ - [ - 4, - 2 - ], - [ - 6, - 1.3 - ], - [ - 20, - 30 - ] - ] - } - } - }, - { - "id": "road_oneway", - "type": "symbol", - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 15, - "filter": [ - "all", - [ - "==", - "oneway", - 1 - ] - ], - "layout": { - "symbol-placement": "line", - "icon-image": "oneway", - "symbol-spacing": 200, - "icon-padding": 2, - "icon-rotation-alignment": "map", - "icon-rotate": 0, - "icon-size": { - "stops": [ - [ - 15, - 0.5 - ], - [ - 19, - 1 - ] - ] - } - }, - "paint": { - "icon-opacity": 0.5 - } - }, - { - "id": "road_oneway_opposite", - "type": "symbol", - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 15, - "filter": [ - "all", - [ - "==", - "oneway", - -1 - ] - ], - "layout": { - "symbol-placement": "line", - "icon-image": "oneway", - "symbol-spacing": 200, - "icon-padding": 2, - "icon-rotation-alignment": "map", - "icon-rotate": 180, - "icon-size": { - "stops": [ - [ - 15, - 0.5 - ], - [ - 19, - 1 - ] - ] - } - }, - "paint": { - "icon-opacity": 0.5 - } - }, - { - "id": "highway_motorway_subtle", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "maxzoom": 6, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "class", - "motorway" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "#181818", - "line-width": { - "base": 1.4, - "stops": [ - [ - 4, - 2 - ], - [ - 6, - 1.3 - ] - ] - } - } - }, - { - "id": "railway_transit", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 16, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "all", - [ - "==", - "class", - "transit" - ], - [ - "!in", - "brunnel", - "tunnel" - ] - ] - ], - "layout": { - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "rgb(35,35,35)", - "line-width": 3 - } - }, - { - "id": "railway_transit_dashline", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 16, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "all", - [ - "==", - "class", - "transit" - ], - [ - "!in", - "brunnel", - "tunnel" - ] - ] - ], - "layout": { - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "rgb(12,12,12)", - "line-dasharray": [ - 3, - 3 - ], - "line-width": 2 - } - }, - { - "id": "railway_minor", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 16, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "all", - [ - "==", - "class", - "rail" - ], - [ - "has", - "service" - ] - ] - ], - "layout": { - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "rgb(35,35,35)", - "line-width": 3 - } - }, - { - "id": "railway_minor_dashline", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 16, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "all", - [ - "==", - "class", - "rail" - ], - [ - "has", - "service" - ] - ] - ], - "layout": { - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "rgb(12,12,12)", - "line-dasharray": [ - 3, - 3 - ], - "line-width": 2 - } - }, - { - "id": "railway", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 13, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "class", - "rail" - ], - [ - "!has", - "service" - ] - ], - "layout": { - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "rgb(35,35,35)", - "line-width": { - "base": 1.3, - "stops": [ - [ - 16, - 3 - ], - [ - 20, - 7 - ] - ] - } - } - }, - { - "id": "railway_dashline", - "type": "line", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation", - "minzoom": 13, - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "class", - "rail" - ], - [ - "!has", - "service" - ] - ], - "layout": { - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-color": "rgb(12,12,12)", - "line-dasharray": [ - 3, - 3 - ], - "line-width": { - "base": 1.3, - "stops": [ - [ - 16, - 2 - ], - [ - 20, - 6 - ] - ] - } - } - }, - { - "id": "highway_name_other", - "type": "symbol", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation_name", - "filter": [ - "all", - [ - "!=", - "class", - "motorway" - ], - [ - "==", - "$type", - "LineString" - ] - ], - "layout": { - "symbol-placement": "line", - "symbol-spacing": 350, - "text-field": "{name:latin} {name:nonlatin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-max-angle": 30, - "text-pitch-alignment": "viewport", - "text-rotation-alignment": "map", - "text-size": 10, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "text-color": "rgba(80, 78, 78, 1)", - "text-halo-blur": 0, - "text-halo-color": "rgba(0, 0, 0, 1)", - "text-halo-width": 1, - "text-translate": [ - 0, - 0 - ] - } - }, - { - "id": "highway_name_motorway", - "type": "symbol", - "metadata": { - "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5" - }, - "source": "openmaptiles", - "source-layer": "transportation_name", - "filter": [ - "all", - [ - "==", - "$type", - "LineString" - ], - [ - "==", - "class", - "motorway" - ] - ], - "layout": { - "symbol-placement": "line", - "symbol-spacing": 350, - "text-field": "{ref}", - "text-font": [ - "Metropolis Light", - "Noto Sans Regular" - ], - "text-pitch-alignment": "viewport", - "text-rotation-alignment": "viewport", - "text-size": 10, - "visibility": "visible" - }, - "paint": { - "text-color": "hsl(0, 0%, 37%)", - "text-translate": [ - 0, - 2 - ] - } - }, - { - "id": "boundary_state", - "type": "line", - "metadata": { - "mapbox:group": "a14c9607bc7954ba1df7205bf660433f" - }, - "source": "openmaptiles", - "source-layer": "boundary", - "filter": [ - "==", - "admin_level", - 4 - ], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "paint": { - "line-blur": 0.4, - "line-color": "hsl(0, 0%, 21%)", - "line-dasharray": [ - 2, - 2 - ], - "line-opacity": 1, - "line-width": { - "base": 1.3, - "stops": [ - [ - 3, - 1 - ], - [ - 22, - 15 - ] - ] - } - } - }, - { - "id": "boundary_country_z0-4", - "type": "line", - "metadata": { - "mapbox:group": "a14c9607bc7954ba1df7205bf660433f" - }, - "source": "openmaptiles", - "source-layer": "boundary", - "maxzoom": 5, - "filter": [ - "all", - [ - "==", - "admin_level", - 2 - ], - [ - "!has", - "claimed_by" - ] - ], - "layout": { - "line-cap": "round", - "line-join": "round" - }, - "paint": { - "line-blur": { - "base": 1, - "stops": [ - [ - 0, - 0.4 - ], - [ - 22, - 4 - ] - ] - }, - "line-color": "hsl(0, 0%, 23%)", - "line-opacity": 1, - "line-width": { - "base": 1.1, - "stops": [ - [ - 3, - 1 - ], - [ - 22, - 20 - ] - ] - } - } - }, - { - "id": "boundary_country_z5-", - "type": "line", - "metadata": { - "mapbox:group": "a14c9607bc7954ba1df7205bf660433f" - }, - "source": "openmaptiles", - "source-layer": "boundary", - "minzoom": 5, - "filter": [ - "==", - "admin_level", - 2 - ], - "layout": { - "line-cap": "round", - "line-join": "round" - }, - "paint": { - "line-blur": { - "base": 1, - "stops": [ - [ - 0, - 0.4 - ], - [ - 22, - 4 - ] - ] - }, - "line-color": "hsl(0, 0%, 23%)", - "line-opacity": 1, - "line-width": { - "base": 1.1, - "stops": [ - [ - 3, - 1 - ], - [ - 22, - 20 - ] - ] - } - } - }, - { - "id": "place_other", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 14, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "in", - "class", - "hamlet", - "isolated_dwelling", - "neighbourhood" - ] - ], - "layout": { - "text-anchor": "center", - "text-field": "{name:latin}\n{name:nonlatin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-justify": "center", - "text-offset": [ - 0.5, - 0 - ], - "text-size": 10, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "text-color": "rgb(101,101,101)", - "text-halo-blur": 1, - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1 - } - }, - { - "id": "place_suburb", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 15, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "==", - "class", - "suburb" - ] - ], - "layout": { - "text-anchor": "center", - "text-field": "{name:latin}\n{name:nonlatin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-justify": "center", - "text-offset": [ - 0.5, - 0 - ], - "text-size": 10, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "text-color": "rgb(101,101,101)", - "text-halo-blur": 1, - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1 - } - }, - { - "id": "place_village", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 14, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "==", - "class", - "village" - ] - ], - "layout": { - "icon-size": 0.4, - "text-anchor": "left", - "text-field": "{name:latin}\n{name:nonlatin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-justify": "left", - "text-offset": [ - 0.5, - 0.2 - ], - "text-size": 10, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "icon-opacity": 0.7, - "text-color": "rgb(101,101,101)", - "text-halo-blur": 1, - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1 - } - }, - { - "id": "place_town", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 15, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "==", - "class", - "town" - ] - ], - "layout": { - "icon-image": { - "base": 1, - "stops": [ - [ - 0, - "circle-11" - ], - [ - 9, - "" - ] - ] - }, - "icon-size": 0.4, - "text-anchor": { - "base": 1, - "stops": [ - [ - 0, - "left" - ], - [ - 8, - "center" - ] - ] - }, - "text-field": "{name:latin}\n{name:nonlatin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-justify": "left", - "text-offset": [ - 0.5, - 0.2 - ], - "text-size": 10, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "icon-opacity": 0.7, - "text-color": "rgb(101,101,101)", - "text-halo-blur": 1, - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1 - } - }, - { - "id": "place_city", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 14, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "==", - "class", - "city" - ], - [ - ">", - "rank", - 3 - ] - ], - "layout": { - "icon-image": { - "base": 1, - "stops": [ - [ - 0, - "circle-11" - ], - [ - 9, - "" - ] - ] - }, - "icon-size": 0.4, - "text-anchor": { - "base": 1, - "stops": [ - [ - 0, - "left" - ], - [ - 8, - "center" - ] - ] - }, - "text-field": "{name:latin}\n{name:nonlatin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-justify": "left", - "text-offset": [ - 0.5, - 0.2 - ], - "text-size": 10, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "icon-opacity": 0.7, - "text-color": "rgb(101,101,101)", - "text-halo-blur": 1, - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1 - } - }, - { - "id": "place_city_large", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 12, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "<=", - "rank", - 3 - ], - [ - "==", - "class", - "city" - ] - ], - "layout": { - "icon-image": { - "base": 1, - "stops": [ - [ - 0, - "circle-11" - ], - [ - 9, - "" - ] - ] - }, - "icon-size": 0.4, - "text-anchor": { - "base": 1, - "stops": [ - [ - 0, - "left" - ], - [ - 8, - "center" - ] - ] - }, - "text-field": "{name:latin}\n{name:nonlatin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-justify": "left", - "text-offset": [ - 0.5, - 0.2 - ], - "text-size": 14, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "icon-opacity": 0.7, - "text-color": "rgb(101,101,101)", - "text-halo-blur": 1, - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1 - } - }, - { - "id": "place_state", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 12, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "==", - "class", - "state" - ] - ], - "layout": { - "text-field": "{name:latin}\n{name:nonlatin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-size": 10, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "text-color": "rgb(101,101,101)", - "text-halo-blur": 1, - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1 - } - }, - { - "id": "place_country_other", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 8, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "==", - "class", - "country" - ], - [ - "!has", - "iso_a2" - ] - ], - "layout": { - "text-field": "{name:latin}", - "text-font": [ - "Metropolis Light Italic", - "Noto Sans Italic" - ], - "text-size": { - "base": 1, - "stops": [ - [ - 0, - 9 - ], - [ - 1, - 11 - ] - ] - }, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "text-color": "rgb(101,101,101)", - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1.4 - } - }, - { - "id": "place_country_minor", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 8, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "==", - "class", - "country" - ], - [ - ">=", - "rank", - 2 - ], - [ - "has", - "iso_a2" - ] - ], - "layout": { - "text-field": "{name:latin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-size": { - "base": 1, - "stops": [ - [ - 0, - 10 - ], - [ - 6, - 12 - ] - ] - }, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "text-color": "rgb(101,101,101)", - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1.4 - } - }, - { - "id": "place_country_major", - "type": "symbol", - "metadata": { - "mapbox:group": "101da9f13b64a08fa4b6ac1168e89e5f" - }, - "source": "openmaptiles", - "source-layer": "place", - "maxzoom": 6, - "filter": [ - "all", - [ - "==", - "$type", - "Point" - ], - [ - "<=", - "rank", - 1 - ], - [ - "==", - "class", - "country" - ], - [ - "has", - "iso_a2" - ] - ], - "layout": { - "text-anchor": "center", - "text-field": "{name:latin}", - "text-font": [ - "Metropolis Regular", - "Noto Sans Regular" - ], - "text-size": { - "base": 1.4, - "stops": [ - [ - 0, - 10 - ], - [ - 3, - 12 - ], - [ - 4, - 14 - ] - ] - }, - "text-transform": "uppercase", - "visibility": "visible" - }, - "paint": { - "text-color": "rgb(101,101,101)", - "text-halo-color": "rgba(0,0,0,0.7)", - "text-halo-width": 1.4 - } - } - ] -} \ No newline at end of file diff --git a/frontend/Caddyfile b/frontend/Caddyfile index b0399b4..b06a05f 100644 --- a/frontend/Caddyfile +++ b/frontend/Caddyfile @@ -24,12 +24,30 @@ file_server } - # مسیریابی SPA — فقط مسیرهای بدون پسوند (مسیرهای اپ) به index.html برمی‌گردند - handle { + # مسیرهای پنل ادمین فقط SPA هستند و فایلی پشتشان نیست؛ پوسته باید با + # وضعیت ۲۰۰ سرو شود وگرنه قاعدهٔ ۴۰۴ مسیرهای ناشناخته پنل را هم می‌بندد + # (و لبهٔ Traefik به‌جای پوسته، صفحهٔ خطای خودش را نشان می‌دهد). فهرست + # مسیرهای ادمین در App.tsx است؛ مسیر تازهٔ ادمین یعنی افزودن الگوی آن‌جا و این‌جا. + # robots.txt از قبل /admin را برای خزنده‌ها می‌بندد؛ این مسیرها ایندکس نمی‌شوند. + @admin path /admin /admin/* + handle @admin { try_files {path} /index.html file_server } + # مسیرهای واقعی اپ همگی سمت بک‌اند سرو می‌شوند (روتر seo ترافیک را می‌برد)؛ + # پس به این‌جا فقط مسیر ناشناخته می‌رسد — باید ۴۰۴ بدهد، نه پوستهٔ ۲۰۰ (نرم‌۴۰۴) + handle { + try_files {path} + file_server + } + + # خطاها با پوستهٔ برند دیده می‌شوند — وضعیت ۴۰۴ حفظ می‌شود تا خزنده ایندکس نکند + handle_errors { + rewrite * /index.html + file_server + } + # index.html و fallback اسپا: بدون کش مرورگر تا هر استقرار فوری دیده شود header Cache-Control "no-cache" # فایل‌های ثابت (فونت/تصویر): یک ساعت کش مرورگر diff --git a/frontend/index.html b/frontend/index.html index ccb2233..35b8dd4 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -19,7 +19,7 @@ @@ -31,6 +31,21 @@ document.documentElement.setAttribute('lang', 'en'); document.documentElement.setAttribute('dir', 'ltr'); } + // نشانی مطلق اسکیمای Organization پوستهٔ استاتیک — این پوسته فقط برای پوستهٔ + // خطا سرو می‌شود؛ صفحات واقعی این اسکریپت را با نسخهٔ غنی دیتابیس جایگزین + // می‌کنند و آن‌جا این بلوک بدون اثر می‌ماند (اسکیمای تزریقی id دارد) + try { + var staticOrg = document.querySelector('script[type="application/ld+json"]:not([id])'); + if (staticOrg) { + var org = JSON.parse(staticOrg.textContent); + if (org['@type'] === 'Organization') { + ['url', 'logo'].forEach(function (k) { + if (org[k] && org[k].charAt(0) === '/') org[k] = location.origin + org[k]; + }); + staticOrg.textContent = JSON.stringify(org); + } + } + } catch (e) {}