feat: update Docker configuration and add deployment documentation for Coolify
This commit is contained in:
@@ -23,6 +23,10 @@
|
||||
/phpstan.neon
|
||||
|
||||
# Local env & secrets
|
||||
# NOTE: .env IS kept in the image — Symfony's Dotenv::bootEnv requires it to
|
||||
# exist. Its APP_ENV=dev default is harmless in prod: compose sets the real
|
||||
# APP_ENV=prod env var, and Dotenv never overwrites an already-set var
|
||||
# (provided FPM clear_env=no, see docker/php/zz-pool.conf).
|
||||
.env.local
|
||||
.env.*.local
|
||||
/config/jwt/*.pem
|
||||
|
||||
+2
-1
@@ -15,7 +15,7 @@ RUN composer install \
|
||||
# ============================================================
|
||||
# Stage 2 — Frontend assets (Webpack Encore / React 19)
|
||||
# ============================================================
|
||||
FROM node:20-alpine AS assets
|
||||
FROM node:22-alpine AS assets
|
||||
WORKDIR /app
|
||||
# vendor is required: package.json references "@symfony/ux-react": "file:vendor/symfony/ux-react/assets"
|
||||
COPY --from=vendor /app/vendor ./vendor
|
||||
@@ -50,6 +50,7 @@ COPY --from=assets /app/public/build ./public/build
|
||||
|
||||
# Container configuration.
|
||||
COPY docker/php/php.ini /usr/local/etc/php/conf.d/zz-app.ini
|
||||
COPY docker/php/zz-pool.conf /usr/local/etc/php-fpm.d/zz-pool.conf
|
||||
COPY docker/nginx/default.conf /etc/nginx/http.d/default.conf
|
||||
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
; Production FPM pool overrides for ClinicPro.
|
||||
; Default www.conf ships clear_env = yes, which strips the container
|
||||
; environment (APP_ENV=prod, DATABASE_URL, ...) from PHP worker processes.
|
||||
; Without this, PHP falls back to the baked .env (APP_ENV=dev) and boots the
|
||||
; wrong environment. Keep the container env so compose-provided vars reach PHP.
|
||||
[www]
|
||||
clear_env = no
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
# راهنمای دیپلوی ClinicPro (Coolify + Docker Compose)
|
||||
|
||||
این راهنما برای دیپلوی بکاند ClinicPro روی **Coolify** با استفاده از `docker-compose.yml` نوشته شده.
|
||||
|
||||
> توجه: این `docker-compose.yml` فقط برای **production** است. محیط لوکال از `compose.yaml` خودِ ddev استفاده میکند — این دو را با هم اشتباه نگیر.
|
||||
|
||||
---
|
||||
|
||||
## معماری استک
|
||||
|
||||
`docker-compose.yml` پنج سرویس بالا میآورد:
|
||||
|
||||
| سرویس | نقش | نکته |
|
||||
|---|---|---|
|
||||
| `app` | PHP-FPM + Nginx (وب) | تنها سرویسی که `RUN_INIT=1` دارد؛ مهاجرت DB و تولید کلید JWT را اجرا میکند. دامنهها را به این سرویس (پورت 80) وصل کن. |
|
||||
| `worker-async` | مصرفکننده صف async (SMS و کارهای async) | `messenger:consume async` |
|
||||
| `worker-scheduler` | زمانبند | هر ۱ دقیقه نوبتهای پرداختنشده را منقضی میکند |
|
||||
| `mariadb` | پایگاهداده MariaDB 11.8 | healthcheck دارد؛ بقیه منتظرش میمانند |
|
||||
| `redis` | صف Messenger + کش | `appendonly yes` (ماندگار) |
|
||||
|
||||
**ولومهای ماندگار** (دادهها در ریدیپلوی حفظ میشوند):
|
||||
|
||||
- `jwt_keys` → کلیدهای JWT
|
||||
- `uploads_public` و `uploads_var` → فایلهای آپلودی
|
||||
- `mariadb_data` → داده DB
|
||||
- `redis_data` → داده Redis
|
||||
|
||||
---
|
||||
|
||||
## پیشنیازها
|
||||
|
||||
- نمونهی Coolify در حال اجرا با Traefik (پیشفرض Coolify).
|
||||
- ریپوی Git متصل به Coolify.
|
||||
- رکوردهای DNS برای دامنهی API و همهی دامنههای فرانتاند که به سرور اشاره کنند.
|
||||
|
||||
---
|
||||
|
||||
## مرحله ۱ — ساخت منبع (Resource) در Coolify
|
||||
|
||||
1. **New Resource → Docker Compose** (Build Pack: `Docker Compose`).
|
||||
2. ریپو و برنچ را انتخاب کن.
|
||||
3. فیلد **Compose file** را روی `docker-compose.yml` بگذار.
|
||||
4. `networks:` سفارشی تعریف **نکن** — شبکه را Coolify مدیریت میکند؛ شبکهی سفارشی روتینگ Traefik را میشکند.
|
||||
|
||||
---
|
||||
|
||||
## مرحله ۲ — دامنهها
|
||||
|
||||
همهی دامنههای سرو شونده را به سرویس **`app`** (پورت 80) اختصاص بده — هم دامنهی API و هم همهی دامنههای فرانتاند. Coolify لیست دامنهی جداشده با کاما را روی یک سرویس قبول میکند و TLS را خودش صادر میکند.
|
||||
|
||||
> اجازهدادن CORS و host فرانتاندها از طریق متغیرهای `CORS_ALLOW_ORIGIN` / `ALLOWED_FRONTEND_HOSTS` کنترل میشود، نه دامنهی Coolify.
|
||||
|
||||
---
|
||||
|
||||
## مرحله ۳ — متغیرهای محیطی
|
||||
|
||||
از `.env.coolify.example` کپی کن و در تب **Environment Variables** منبع Coolify بگذار.
|
||||
|
||||
فقط متغیرهایی که در `docker-compose.yml` بهصورت `${...}` ارجاع شدهاند لازماند. `DATABASE_URL` / `MESSENGER_TRANSPORT_DSN` / `REDIS_URL` داخل خود compose از روی نام سرویسها ساخته میشوند.
|
||||
|
||||
### اسرار (الزامی — قبل از اولین دیپلوی)
|
||||
|
||||
```bash
|
||||
APP_SECRET= # php -r "echo bin2hex(random_bytes(32));"
|
||||
JWT_PASSPHRASE= # openssl rand -hex 32 (باید قبل از اولین استارت موجود باشد؛ کلید JWT با همین ساخته میشود)
|
||||
DB_PASSWORD= # رمز کاربر DB اپلیکیشن
|
||||
DB_ROOT_PASSWORD= # رمز root مریادیبی
|
||||
```
|
||||
|
||||
> ⚠️ `JWT_PASSPHRASE` را بعد از اولین دیپلوی عوض نکن — کلید JWT یکبار با همین passphrase تولید و روی ولوم `jwt_keys` ماندگار میشود. تغییرش همهی توکنها را میشکند.
|
||||
|
||||
در Coolify میتوانی بهجای هاردکد از magic var استفاده کنی:
|
||||
|
||||
```bash
|
||||
DB_PASSWORD=${SERVICE_PASSWORD_DB}
|
||||
APP_SECRET=${SERVICE_HEX_APPSECRET}
|
||||
```
|
||||
|
||||
### دامنهها و CORS
|
||||
|
||||
```bash
|
||||
APP_BASE_URL=https://api.nobat724.com # دامنهی خودِ بکاند (برای callback پرداخت و URLهای مطلق)
|
||||
```
|
||||
|
||||
`ALLOWED_FRONTEND_HOSTS` و `CORS_ALLOW_ORIGIN` از `docker/frontend-domains.json` **تولید** میشوند. برای اضافه/حذف دامنهی شهر:
|
||||
|
||||
```bash
|
||||
# آن فایل را ویرایش کن، سپس:
|
||||
php docker/gen-cors-env.php # روی سرور
|
||||
# یا لوکال:
|
||||
ddev exec php docker/gen-cors-env.php
|
||||
```
|
||||
|
||||
خروجی را در Coolify جایگزین کن.
|
||||
|
||||
### ریورسپراکسی
|
||||
|
||||
```bash
|
||||
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1
|
||||
```
|
||||
|
||||
تا Symfony به هدرهای `X-Forwarded-*` ترافیک اعتماد کند.
|
||||
|
||||
### api.ir (استعلام هویت — Shahkar / IbanMatch)
|
||||
|
||||
```bash
|
||||
API_IR_BASE_URL=https://s.api.ir
|
||||
API_IR_TOKEN= # خالی => fail-closed (تأیید نماینده رد میشود)
|
||||
```
|
||||
|
||||
### چیزهایی که env لازم ندارند
|
||||
|
||||
- کلیدهای SMS (kavenegar/rangineh) و درگاه پرداخت (mellat/sep) از **DB ("تنظیمات سایت")** خوانده میشوند، نه env.
|
||||
- `REFRESH_TOKEN_TTL` / `OTP_TTL` / `MAX_FILE_SIZE_BYTES` در خود compose ثابتاند.
|
||||
|
||||
---
|
||||
|
||||
## مرحله ۴ — دیپلوی
|
||||
|
||||
روی **Deploy** بزن. در اولین استارت بهصورت خودکار اینها اتفاق میافتد (`entrypoint.sh` + `RUN_INIT=1` روی سرویس `app`):
|
||||
|
||||
1. مالکیت `var`, `public/uploads`, `config/jwt` به `www-data` داده میشود.
|
||||
2. کلید JWT اگر روی ولوم نباشد ساخته میشود (`--skip-if-exists`).
|
||||
3. کش prod پاک و warmup میشود.
|
||||
4. مهاجرتهای DB با `--all-or-nothing` اعمال میشوند (ترنزکشن).
|
||||
|
||||
> ورکرها `RUN_INIT=0` دارند تا مهاجرت/تولید کلید با هم تداخل نکنند.
|
||||
|
||||
---
|
||||
|
||||
## مرحله ۵ — پس از اولین دیپلوی
|
||||
|
||||
### ساخت اولین ادمین
|
||||
|
||||
```bash
|
||||
# داخل کانتینر سرویس app
|
||||
php bin/console app:create-admin
|
||||
```
|
||||
|
||||
### بررسی سلامت
|
||||
|
||||
- healthcheck سرویس `app`: `php fsockopen 127.0.0.1:80`.
|
||||
- Swagger: `https://<APP_BASE_URL>/api/doc`
|
||||
- پنل ادمین: `https://<APP_BASE_URL>/admin`
|
||||
|
||||
---
|
||||
|
||||
## دیپلویهای بعدی
|
||||
|
||||
push روی برنچ متصل (یا Deploy دستی). در هر ریدیپلوی:
|
||||
|
||||
- ایمیج دوباره build میشود (vendor + اسمبل فرانتاند multi-stage).
|
||||
- مهاجرتهای جدید روی استارت `app` اعمال میشوند.
|
||||
- ولومها حفظ میشوند (DB، آپلودها، کلیدهای JWT، Redis سالم میمانند).
|
||||
|
||||
---
|
||||
|
||||
## عیبیابی
|
||||
|
||||
| نشانه | علت محتمل |
|
||||
|---|---|
|
||||
| ارورهای CORS در فرانت | `CORS_ALLOW_ORIGIN` با دامنه نمیخواند؛ از `gen-cors-env.php` بازتولید کن |
|
||||
| `app` بالا نمیآید، منتظر DB میماند | healthcheck `mariadb` رد نشده؛ لاگ mariadb را ببین |
|
||||
| ۴۰۱/توکن نامعتبر بعد از ریدیپلوی | `JWT_PASSPHRASE` تغییر کرده یا ولوم `jwt_keys` پاک شده |
|
||||
| IPها/HTTPS اشتباه پشت پراکسی | `TRUSTED_PROXIES` ست نشده |
|
||||
| مهاجرت اجرا نشد | فقط `app` با `RUN_INIT=1` اجرا میکند؛ مطمئن شو override نشده |
|
||||
| تأیید نماینده رد میشود | `API_IR_TOKEN` خالی است (fail-closed) |
|
||||
Generated
+4
-6
@@ -49,12 +49,6 @@
|
||||
"typescript": "^5.0.0",
|
||||
"webpack": "^5.72",
|
||||
"webpack-cli": "^6.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-darwin-arm64": "^4.3.0",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "^4.3.0",
|
||||
"lightningcss-darwin-arm64": "^1.32.0",
|
||||
"lightningcss-linux-arm64-gnu": "^1.32.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@adobe/css-tools": {
|
||||
@@ -15902,6 +15896,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15969,6 +15964,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -20035,6 +20031,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -20118,6 +20115,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
||||
@@ -44,12 +44,6 @@
|
||||
"zod": "^3.0.0",
|
||||
"zustand": "^5.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-darwin-arm64": "^4.3.0",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "^4.3.0",
|
||||
"lightningcss-darwin-arm64": "^1.32.0",
|
||||
"lightningcss-linux-arm64-gnu": "^1.32.0"
|
||||
},
|
||||
"license": "UNLICENSED",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<svg viewbox="0 0 24 24" fill="none"><path d="M12 21s-7-4.3-7-9.5A4 4 0 0 1 12 8a4 4 0 0 1 7 3.5C19 16.7 12 21 12 21Z" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/></svg>
|
||||
</span>
|
||||
<div>
|
||||
<b class="mono">۹۵٪</b>
|
||||
<b style="font-variant-numeric:tabular-nums">۹۵٪</b>
|
||||
<span>رضایت بیماران</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,7 +68,7 @@
|
||||
<svg viewbox="0 0 24 24" fill="none"><path d="M8 7V5m8 2V5M4 9h16M5 7h14v12a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7Z" stroke="currentColor" stroke-width="1.9"/></svg>
|
||||
</span>
|
||||
<div>
|
||||
<b class="mono">۳۸</b>
|
||||
<b style="font-variant-numeric:tabular-nums">۳۸</b>
|
||||
<span>نوبت امروز</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -553,10 +553,12 @@ var btn2 = document.getElementById('openRegModal2');
|
||||
if (btn1)
|
||||
btn1.addEventListener('click', openRegModal);
|
||||
|
||||
|
||||
if (btn2)
|
||||
btn2.addEventListener('click', openRegModal);
|
||||
|
||||
|
||||
|
||||
document.querySelectorAll('.reg-card').forEach(function (card) {
|
||||
card.addEventListener('click', function () {
|
||||
document.querySelectorAll('.reg-card').forEach(function (c) {
|
||||
@@ -567,6 +569,7 @@ var radio = card.querySelector('input[type=radio]');
|
||||
if (radio)
|
||||
radio.checked = true;
|
||||
|
||||
|
||||
selectedType = card.dataset.type;
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.style.opacity = '1';
|
||||
@@ -582,6 +585,7 @@ var msg = document.getElementById('regMsg');
|
||||
if (! selectedType)
|
||||
return;
|
||||
|
||||
|
||||
if (! name) {
|
||||
showMsg('نام را وارد کنید', 'error');
|
||||
return;
|
||||
@@ -613,7 +617,9 @@ return r.json();
|
||||
if (data.success) {
|
||||
showMsg('درخواست شما ثبت شد. پس از بررسی، اطلاعات ورود از طریق SMS ارسال میشود.', 'success');
|
||||
submitBtn.textContent = 'ارسال شد ✓';
|
||||
setTimeout(function() { closeRegModal(); }, 2200);
|
||||
setTimeout(function () {
|
||||
closeRegModal();
|
||||
}, 2200);
|
||||
} else {
|
||||
var errMsg = (data.errors && data.errors[0]) ? data.errors[0].message : 'خطایی رخ داد';
|
||||
showMsg(errMsg, 'error');
|
||||
@@ -663,6 +669,8 @@ msg.style.border = type === 'success' ? '1px solid #bbf7d0' : '1px solid #fecaca
|
||||
<svg viewbox="0 0 24 24" fill="none"><path d="M4 20l1.4-4A8 8 0 1 1 9 18.6L4 20Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
<!-- نماد اعتماد الکترونیکی — بعد از لود کامل صفحه تزریق میشود (footer JS) -->
|
||||
<div id="enamad-seal" style="margin-top:18px"></div>
|
||||
</div>
|
||||
|
||||
<div class="footer-col">
|
||||
@@ -684,7 +692,7 @@ msg.style.border = type === 'success' ? '1px solid #bbf7d0' : '1px solid #fecaca
|
||||
</div>
|
||||
<div class="fl">
|
||||
<svg viewbox="0 0 24 24" fill="none"><path d="M4 6.5C4 5 5 4 6.5 4h2c.6 0 1.1.4 1.3 1l1 3c.2.6 0 1.2-.5 1.6L9 11c1 2 2.5 3.5 4.5 4.5l1.4-1.3c.4-.4 1-.6 1.6-.4l3 1c.6.2 1 .7 1 1.3v2c0 1.5-1 2.5-2.5 2.4C10.5 20 4 13.5 4 6.5Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg>
|
||||
<a href="tel:07491012178" class="mono" dir="ltr">۰۷۴۹۱۰۱۲۱۷۸</a>
|
||||
<a href="tel:07491012178" style="font-variant-numeric:tabular-nums" dir="ltr">۰۷۴۹۱۰۱۲۱۷۸</a>
|
||||
</div>
|
||||
<div class="fl">
|
||||
<svg viewbox="0 0 24 24" fill="none"><rect x="3" y="5" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.7"/><path d="m4 7 8 5 8-5" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg>
|
||||
@@ -764,6 +772,7 @@ navLinks.style.boxShadow = '0 16px 30px oklch(0.4 0.08 285 / 0.1)';
|
||||
});
|
||||
|
||||
|
||||
|
||||
var sections = [
|
||||
'hero',
|
||||
'features',
|
||||
@@ -780,6 +789,7 @@ var el = document.getElementById(id);
|
||||
if (el && el.offsetTop <= y)
|
||||
cur = id;
|
||||
|
||||
|
||||
});
|
||||
links.forEach(function (a) {
|
||||
a.classList.toggle('active', a.getAttribute('href') === '#' + cur);
|
||||
@@ -805,6 +815,7 @@ reveals.forEach(function (el) {
|
||||
if (inView(el))
|
||||
reveal(el);
|
||||
|
||||
|
||||
});
|
||||
if ('IntersectionObserver' in window) {
|
||||
var io = new IntersectionObserver(function (es) {
|
||||
@@ -822,6 +833,7 @@ reveals.forEach(function (el) {
|
||||
if (! el.classList.contains('in'))
|
||||
io.observe(el);
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
var ticking = false;
|
||||
@@ -829,12 +841,14 @@ function sweep() {
|
||||
if (ticking)
|
||||
return;
|
||||
|
||||
|
||||
ticking = true;
|
||||
requestAnimationFrame(function () {
|
||||
reveals.forEach(function (el) {
|
||||
if (inView(el))
|
||||
reveal(el);
|
||||
|
||||
|
||||
});
|
||||
ticking = false;
|
||||
});
|
||||
@@ -845,6 +859,33 @@ setTimeout(function () {
|
||||
reveals.forEach(reveal);
|
||||
}, 2800);
|
||||
}
|
||||
|
||||
// نماد اعتماد الکترونیکی (Enamad) — فقط بعد از لود کامل صفحه تزریق میشود تا
|
||||
// درخواست تصویرِ دامنهی بیرونی با منابع خود سایت رقابت نکند و سرعت لود پایین نیاید.
|
||||
function mountEnamad() {
|
||||
var slot = document.getElementById('enamad-seal');
|
||||
if (! slot)
|
||||
return;
|
||||
|
||||
var a = document.createElement('a');
|
||||
a.setAttribute('referrerpolicy', 'origin');
|
||||
a.target = '_blank';
|
||||
a.href = 'https://trustseal.enamad.ir/?id=648891&Code=tudyxbILEtrmS2VZHeGr1LfmkrEerqeF';
|
||||
var img = document.createElement('img');
|
||||
img.setAttribute('referrerpolicy', 'origin');
|
||||
img.src = 'https://trustseal.enamad.ir/logo.aspx?id=648891&Code=tudyxbILEtrmS2VZHeGr1LfmkrEerqeF';
|
||||
img.alt = 'نماد اعتماد الکترونیکی';
|
||||
img.style.cursor = 'pointer';
|
||||
img.setAttribute('code', 'tudyxbILEtrmS2VZHeGr1LfmkrEerqeF');
|
||||
img.loading = 'lazy';
|
||||
a.appendChild(img);
|
||||
slot.appendChild(a);
|
||||
}
|
||||
if (document.readyState === 'complete') {
|
||||
mountEnamad();
|
||||
} else {
|
||||
window.addEventListener('load', mountEnamad);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2744,12 +2744,12 @@
|
||||
source-map-js "^1.2.1"
|
||||
tailwindcss "4.3.0"
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64@^4.3.0", "@tailwindcss/oxide-darwin-arm64@4.3.0":
|
||||
"@tailwindcss/oxide-darwin-arm64@4.3.0":
|
||||
version "4.3.0"
|
||||
resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz"
|
||||
integrity sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu@^4.3.0", "@tailwindcss/oxide-linux-arm64-gnu@4.3.0":
|
||||
"@tailwindcss/oxide-linux-arm64-gnu@4.3.0":
|
||||
version "4.3.0"
|
||||
resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz"
|
||||
integrity sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==
|
||||
@@ -4849,12 +4849,12 @@ leaflet@^1.9.0, leaflet@^1.9.4:
|
||||
resolved "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz"
|
||||
integrity sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==
|
||||
|
||||
lightningcss-darwin-arm64@^1.32.0, lightningcss-darwin-arm64@1.32.0:
|
||||
lightningcss-darwin-arm64@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz"
|
||||
integrity sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==
|
||||
|
||||
lightningcss-linux-arm64-gnu@^1.32.0, lightningcss-linux-arm64-gnu@1.32.0:
|
||||
lightningcss-linux-arm64-gnu@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz"
|
||||
integrity sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==
|
||||
|
||||
Reference in New Issue
Block a user