2 Commits
Author SHA1 Message Date
Amirmahdi dae31e9e83 feat: add assetlinks.json 2026-06-21 20:21:33 +03:30
Amirmahdi fc30597835 feat: logo 2026-06-21 19:04:29 +03:30
16 changed files with 249 additions and 53 deletions
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Runs on `git push`. If a tag ref is among what's being pushed, build locally
# and deploy to the server. Normal branch pushes are untouched.
set -euo pipefail
deploy=0
tag=""
while read -r local_ref _local_sha _remote_ref _remote_sha; do
case "$local_ref" in
refs/tags/*)
deploy=1
tag="${local_ref#refs/tags/}"
;;
esac
done
if [ "$deploy" = "1" ]; then
echo "→ Tag '$tag' is being pushed — building locally and deploying to the server…"
exec "$(git rev-parse --show-toplevel)/scripts/deploy.sh"
fi
exit 0
+2 -2
View File
@@ -16,8 +16,8 @@ function App() {
<> <>
<SEOHead <SEOHead
title="آرام لند - تمرین تنفسی و مدیتیشن" title="آرام جان - تمرین تنفسی و مدیتیشن"
description="اپلیکیشن آرام لند - همراه شما برای رسیدن به آرامش درونی با مدیتیشن‌های فارسی، تمرین‌های تنفسی و موسیقی آرامش‌بخش" description="اپلیکیشن آرام جان - همراه شما برای رسیدن به آرامش درونی با مدیتیشن‌های فارسی، تمرین‌های تنفسی و موسیقی آرامش‌بخش"
canonical="/" ogImage="../app/og-image.png" /> canonical="/" ogImage="../app/og-image.png" />
<div className="min-h-screen bg-gradient-to-b from-blue-50 via-white to-green-50" dir="rtl"> <div className="min-h-screen bg-gradient-to-b from-blue-50 via-white to-green-50" dir="rtl">
+1 -1
View File
@@ -15,7 +15,7 @@ function CallToAction() {
آماده برای <span className="text-primary-500">تغییر</span> زندگی؟ آماده برای <span className="text-primary-500">تغییر</span> زندگی؟
</h2> </h2>
<p className="text-xl text-gray-700 mb-8 max-w-2xl mx-auto leading-relaxed"> <p className="text-xl text-gray-700 mb-8 max-w-2xl mx-auto leading-relaxed">
امروز اولین قدم رو بردار و ببین چطور آرام لند میتونه کیفیت زندگیات رو متحول کنه امروز اولین قدم رو بردار و ببین چطور آرام جان میتونه کیفیت زندگیات رو متحول کنه
</p> </p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center"> <div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
+1 -1
View File
@@ -47,7 +47,7 @@ function DownloadSection() {
همین حالا <span className="text-primary-500">شروع کنید</span> همین حالا <span className="text-primary-500">شروع کنید</span>
</h2> </h2>
<p className="text-xl text-gray-600 max-w-2xl mx-auto"> <p className="text-xl text-gray-600 max-w-2xl mx-auto">
آرام لند را روی دستگاه مورد علاقهتان نصب کنید آرام جان را روی دستگاه مورد علاقهتان نصب کنید
</p> </p>
</div> </div>
+5 -6
View File
@@ -1,5 +1,6 @@
"use client"; "use client";
import { useScroll } from '../hooks/useScroll'; import { useScroll } from '../hooks/useScroll';
import LotusLogo from './LotusLogo';
function Footer() { function Footer() {
const { scrollToSectionWithHeader } = useScroll(); const { scrollToSectionWithHeader } = useScroll();
@@ -12,11 +13,9 @@ function Footer() {
{/* Brand Column */} {/* Brand Column */}
<div className="lg:col-span-2"> <div className="lg:col-span-2">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="bg-gradient-to-r from-primary-500 to-primary-600 w-10 h-10 rounded-xl flex items-center justify-center"> <LotusLogo className="w-11 h-11 drop-shadow-sm" />
<span className="text-white font-bold text-lg">آ</span>
</div>
<div className="text-2xl font-bold bg-gradient-to-r from-primary-500 to-primary-600 bg-clip-text text-transparent"> <div className="text-2xl font-bold bg-gradient-to-r from-primary-500 to-primary-600 bg-clip-text text-transparent">
آرام لند آرام جان
</div> </div>
</div> </div>
<p className="text-gray-600 leading-relaxed mb-6 max-w-md"> <p className="text-gray-600 leading-relaxed mb-6 max-w-md">
@@ -44,7 +43,7 @@ function Footer() {
{[ {[
{ name: 'ویژگی‌ها', section: 'features' }, { name: 'ویژگی‌ها', section: 'features' },
{ name: 'امکانات', section: 'what-you-can-do' }, { name: 'امکانات', section: 'what-you-can-do' },
{ name: 'چرا آرام لند', section: 'why-aramland' }, { name: 'چرا آرام جان', section: 'why-aramland' },
{ name: 'ارتباط با تیم', href: 'https://approagency.ir/' } { name: 'ارتباط با تیم', href: 'https://approagency.ir/' }
].map((link) => ( ].map((link) => (
<li key={link.name}> <li key={link.name}>
@@ -107,7 +106,7 @@ function Footer() {
<div className="flex flex-col md:flex-row justify-between items-center gap-4"> <div className="flex flex-col md:flex-row justify-between items-center gap-4">
<div className="text-center md:text-right"> <div className="text-center md:text-right">
<p className="text-gray-600 mb-2"> <p className="text-gray-600 mb-2">
© 1404 آرام لند - همراه تو برای یک زندگی آرامتر © 1404 آرام جان - همراه تو برای یک زندگی آرامتر
</p> </p>
<p className="text-gray-500 text-sm"> <p className="text-gray-500 text-sm">
ساخته شده با عشق برای مردم ایران ساخته شده با عشق برای مردم ایران
+3 -4
View File
@@ -2,6 +2,7 @@
import { useScroll } from '../hooks/useScroll'; import { useScroll } from '../hooks/useScroll';
import { useState } from 'react'; import { useState } from 'react';
import { Menu, X } from 'lucide-react'; import { Menu, X } from 'lucide-react';
import LotusLogo from './LotusLogo';
function Header() { function Header() {
const { scrollToSectionWithHeader } = useScroll(); const { scrollToSectionWithHeader } = useScroll();
@@ -26,11 +27,9 @@ function Header() {
<div className="max-w-6xl mx-auto flex justify-between items-center"> <div className="max-w-6xl mx-auto flex justify-between items-center">
{/* Logo */} {/* Logo */}
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="bg-gradient-to-r from-primary-500 to-primary-600 w-8 h-8 rounded-xl flex items-center justify-center"> <LotusLogo className="w-9 h-9 drop-shadow-sm" />
<span className="text-white font-bold text-sm">آ</span>
</div>
<div className="text-xl sm:text-2xl font-bold bg-gradient-to-r from-primary-500 to-primary-600 bg-clip-text text-transparent"> <div className="text-xl sm:text-2xl font-bold bg-gradient-to-r from-primary-500 to-primary-600 bg-clip-text text-transparent">
آرام لند آرام جان
</div> </div>
</div> </div>
+26 -16
View File
@@ -1,6 +1,7 @@
"use client"; "use client";
import DownloadSection from "./DownloadSection"; import DownloadSection from "./DownloadSection";
import { useScroll } from '../hooks/useScroll'; import { useScroll } from '../hooks/useScroll';
import LotusLogo from "./LotusLogo";
function Hero() { function Hero() {
const { scrollToSectionWithHeader } = useScroll(); const { scrollToSectionWithHeader } = useScroll();
return ( return (
@@ -8,28 +9,37 @@ function Hero() {
{/* Background decoration */} {/* Background decoration */}
<div className="absolute inset-0 overflow-hidden pointer-events-none rounded-3xl"> <div className="absolute inset-0 overflow-hidden pointer-events-none rounded-3xl">
<div className="absolute -top-20 -right-20 w-72 h-72 bg-primary-500 opacity-10 rounded-full blur-3xl"></div> <div className="absolute -top-20 -right-20 w-72 h-72 bg-primary-500 opacity-10 rounded-full blur-3xl"></div>
<div className="absolute -bottom-20 -left-20 w-72 h-72 bg-primary-500 opacity-10 rounded-full blur-3xl"></div> <div className="absolute -bottom-20 -left-20 w-72 h-72 bg-primary-400 opacity-10 rounded-full blur-3xl"></div>
</div> </div>
<div className="relative"> <div className="relative">
<div className="mb-8 animate-float"> {/* App logo */}
<div className="relative inline-block"> <div className="mb-6 flex justify-center">
<div className="relative inline-block animate-float">
<div className="absolute inset-0 bg-gradient-to-r from-primary-500 to-primary-600 opacity-20 blur-xl rounded-full"></div> <div className="absolute inset-0 bg-gradient-to-r from-primary-500 to-primary-600 blur-2xl rounded-full animate-pulseGlow"></div>
<div className="relative w-24 h-24 sm:w-28 sm:h-28 rounded-[26px] overflow-hidden shadow-2xl shadow-primary-500/40">
<LotusLogo className="w-full h-full" />
</div>
</div> </div>
</div> </div>
<h1 className="text-5xl md:text-7xl font-bold text-gray-900 mb-1 leading-tight animate-fadeInUp"> {/* Badge */}
آرامش را در <div className="inline-flex items-center gap-2 bg-primary-500/10 text-primary-600 px-4 py-2 rounded-full text-sm font-medium mb-6 animate-fadeInUp">
<span>🌿</span>
مدیتیشن، تمرین تنفسی و آرامش فارسی
</div>
<h1 className="text-5xl md:text-7xl font-bold text-gray-900 mb-4 leading-tight animate-fadeInUp">
آرامش را در
<span className="bg-gradient-to-r from-primary-500 to-primary-600 bg-clip-text text-transparent"> هر لحظه </span> <span className="bg-gradient-to-r from-primary-500 to-primary-600 bg-clip-text text-transparent"> هر لحظه </span>
تجربه کنید تجربه کنید
</h1> </h1>
<p className="text-lg md:text-xl text-gray-600 max-w-2xl mx-auto animate-fadeInUp delay-100">
با آرام جان هر روز چند دقیقه برای خودت وقت بگذار؛ ذهنی آرامتر، خوابی عمیقتر و انرژیای تازه.
</p>
<DownloadSection /> <DownloadSection />
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center animate-fadeInUp delay-200"> <div className="flex flex-col sm:flex-row gap-4 justify-center items-center animate-fadeInUp delay-200">
{/* <button className="bg-gradient-to-r from-primary-500 to-primary-600 text-white font-bold py-4 px-8 rounded-2xl hover:shadow-2xl transition-all duration-300 transform hover:scale-105 text-lg shadow-lg hover:shadow-primary-500/25"> <button onClick={() => scrollToSectionWithHeader('what-you-can-do')} className="bg-gradient-to-r from-primary-500 to-primary-600 border-2 border-transparent text-white font-bold py-4 px-8 rounded-2xl hover:shadow-2xl hover:shadow-primary-500/30 transition-all duration-300 transform hover:scale-105">
شروع کنید رایگان
</button> */}
<button onClick={() => scrollToSectionWithHeader('what-you-can-do')} className=" bg-gradient-to-r from-primary-500 to-primary-600 border-2 text-white font-bold py-4 px-8 rounded-2xl hover:border-primary-200 hover:text-primary-50 transition-all duration-300">
مشاهده ویژگیها مشاهده ویژگیها
</button> </button>
</div> </div>
@@ -37,11 +47,11 @@ function Hero() {
{/* Trust indicators */} {/* Trust indicators */}
<div className="mt-12 animate-fadeInUp delay-300"> <div className="mt-12 animate-fadeInUp delay-300">
<p className="text-gray-500 text-sm mb-4">مورد اعتماد هزاران کاربر</p> <p className="text-gray-500 text-sm mb-4">مورد اعتماد هزاران کاربر</p>
<div className="flex justify-center items-center gap-8 opacity-60"> <div className="flex justify-center items-center gap-8 opacity-80">
<div className="text-2xl font-bold text-primary-500"> ۴</div> <div className="text-2xl font-bold text-primary-500"> ۴</div>
<div className="h-4 w-px bg-primary-500"></div> <div className="h-4 w-px bg-primary-300"></div>
<div className="text-primary-500 font-bold">+۱۰۰۰ دانلود</div> <div className="text-primary-500 font-bold">+۱۰۰۰ دانلود</div>
<div className="h-4 w-px bg-primary-500"></div> <div className="h-4 w-px bg-primary-300"></div>
<div className="text-primary-500 font-bold ">۹۸٪ رضایت</div> <div className="text-primary-500 font-bold ">۹۸٪ رضایت</div>
</div> </div>
</div> </div>
+43
View File
@@ -0,0 +1,43 @@
// Brand mark for آرام جان — a layered translucent lotus on a blue→indigo
// gradient, matching the app icon. Self-contained SVG so it stays crisp at any
// size and needs no image asset. Size it with `className` (e.g. "w-9 h-9").
function LotusLogo({ className = "w-9 h-9", title = "آرام جان" }) {
const petal = "M50 72 C42 56 42 34 50 22 C58 34 58 56 50 72 Z";
const angles = [-50, -25, 0, 25, 50];
const opacityFor = (a) => {
if (a === 0) return 0.95;
if (Math.abs(a) === 25) return 0.72;
return 0.5;
};
return (
<svg
viewBox="0 0 100 100"
className={className}
xmlns="http://www.w3.org/2000/svg"
role="img"
aria-label={title}
>
<defs>
<linearGradient id="aramjan-lotus-bg" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#5fa0ff" />
<stop offset="100%" stopColor="#6a4ff0" />
</linearGradient>
</defs>
<rect width="100" height="100" rx="26" fill="url(#aramjan-lotus-bg)" />
<g fill="#ffffff">
{angles.map((a) => (
<path
key={a}
d={petal}
transform={`rotate(${a} 50 72)`}
fillOpacity={opacityFor(a)}
/>
))}
</g>
</svg>
);
}
export default LotusLogo;
+3 -3
View File
@@ -60,10 +60,10 @@ function WhyAramland() {
<div className="text-center mb-16"> <div className="text-center mb-16">
<div className="inline-flex items-center gap-2 bg-primary-500/10 text-primary-500 px-4 py-2 rounded-full text-sm font-medium mb-4"> <div className="inline-flex items-center gap-2 bg-primary-500/10 text-primary-500 px-4 py-2 rounded-full text-sm font-medium mb-4">
<span className="text-lg">🚀</span> <span className="text-lg">🚀</span>
برتری آرام لند برتری آرام جان
</div> </div>
<h2 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4"> <h2 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
چرا <span className="text-primary-500">هزاران نفر</span> آرام لند را انتخاب میکنند؟ چرا <span className="text-primary-500">هزاران نفر</span> آرام جان را انتخاب میکنند؟
</h2> </h2>
<p className="text-xl text-gray-600 max-w-2xl mx-auto"> <p className="text-xl text-gray-600 max-w-2xl mx-auto">
تجربهای متفاوت از آرامش، طراحی شده مخصوص فارسیزبانان تجربهای متفاوت از آرامش، طراحی شده مخصوص فارسیزبانان
@@ -129,7 +129,7 @@ function WhyAramland() {
<div className="flex flex-col md:flex-row items-center justify-between gap-6"> <div className="flex flex-col md:flex-row items-center justify-between gap-6">
<div className="flex-1"> <div className="flex-1">
<h4 className="text-2xl font-bold text-gray-900 mb-3"> <h4 className="text-2xl font-bold text-gray-900 mb-3">
👥 عضو جامعه هزاران نفری آرام لند شوید 👥 عضو جامعه هزاران نفری آرام جان شوید
</h4> </h4>
<p className="text-gray-700 leading-relaxed"> <p className="text-gray-700 leading-relaxed">
به خانوادهای از کاربران خوشحال بپیوندید که هر روز قدمی به سمت آرامش برمیدارند به خانوادهای از کاربران خوشحال بپیوندید که هر روز قدمی به سمت آرامش برمیدارند
+46
View File
@@ -68,4 +68,50 @@ body {
color: var(--foreground); color: var(--foreground);
font-family: 'Dana', 'Inter', Arial, Helvetica, sans-serif; font-family: 'Dana', 'Inter', Arial, Helvetica, sans-serif;
font-weight: 400; font-weight: 400;
}
/* ===== Brand motion =====
The markup already references these animation classes; define them here so
the landing page feels alive (entrance reveals, floating logo, soft glow). */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideInLeft {
from { opacity: 0; transform: translateX(-40px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideInRight {
from { opacity: 0; transform: translateX(40px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes floatY {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-14px); }
}
@keyframes pulseGlow {
0%, 100% { opacity: .3; transform: scale(1); }
50% { opacity: .6; transform: scale(1.08); }
}
.animate-fadeInUp { animation: fadeInUp 0.7s ease-out both; }
.animate-slideInLeft { animation: slideInLeft 0.7s ease-out both; }
.animate-slideInRight { animation: slideInRight 0.7s ease-out both; }
.animate-float { animation: floatY 5s ease-in-out infinite; }
.animate-pulseGlow { animation: pulseGlow 4s ease-in-out infinite; }
/* Stagger helpers — apply animation-delay on top of the entrance animations */
.delay-100 { animation-delay: .1s; }
.delay-200 { animation-delay: .2s; }
.delay-300 { animation-delay: .3s; }
.delay-400 { animation-delay: .4s; }
@media (prefers-reduced-motion: reduce) {
.animate-fadeInUp,
.animate-slideInLeft,
.animate-slideInRight,
.animate-float,
.animate-pulseGlow {
animation: none;
}
} }
+16
View File
@@ -0,0 +1,16 @@
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="آرام جان">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#5fa0ff"/>
<stop offset="100%" stop-color="#6a4ff0"/>
</linearGradient>
</defs>
<rect width="100" height="100" rx="22" fill="url(#bg)"/>
<g fill="#ffffff">
<path d="M50 72 C42 56 42 34 50 22 C58 34 58 56 50 72 Z" transform="rotate(-50 50 72)" fill-opacity="0.5"/>
<path d="M50 72 C42 56 42 34 50 22 C58 34 58 56 50 72 Z" transform="rotate(-25 50 72)" fill-opacity="0.72"/>
<path d="M50 72 C42 56 42 34 50 22 C58 34 58 56 50 72 Z" transform="rotate(0 50 72)" fill-opacity="0.95"/>
<path d="M50 72 C42 56 42 34 50 22 C58 34 58 56 50 72 Z" transform="rotate(25 50 72)" fill-opacity="0.72"/>
<path d="M50 72 C42 56 42 34 50 22 C58 34 58 56 50 72 Z" transform="rotate(50 50 72)" fill-opacity="0.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 950 B

+18 -19
View File
@@ -2,12 +2,12 @@ import type { Metadata } from 'next';
import './globals.css'; import './globals.css';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'آرام لند | تمرین تنفسی و مدیتیشن', title: 'آرام جان | تمرین تنفسی و مدیتیشن',
description: 'لحظه‌ای آرام بگیر، زندگی را حس کن', description: 'لحظه‌ای آرام بگیر، زندگی را حس کن',
keywords: ['مدیتیشن فارسی', 'آرامش', 'تمرین تنفسی', 'کاهش استرس', 'ذهن‌آگاهی', 'مدیتیشن هدایت شده', 'سلامت روان', 'آرام لند'], keywords: ['مدیتیشن فارسی', 'آرامش', 'تمرین تنفسی', 'کاهش استرس', 'ذهن‌آگاهی', 'مدیتیشن هدایت شده', 'سلامت روان', 'آرام جان'],
authors: [{ name: 'آرام لند' }], authors: [{ name: 'آرام جان' }],
creator: 'آرام لند', creator: 'آرام جان',
publisher: 'آرام لند', publisher: 'آرام جان',
metadataBase: new URL('https://aramland.approagency.ir/'), metadataBase: new URL('https://aramland.approagency.ir/'),
formatDetection: { formatDetection: {
email: false, email: false,
@@ -15,7 +15,7 @@ export const metadata: Metadata = {
telephone: false, telephone: false,
}, },
icons: { icons: {
icon: '/favicon.png', icon: '/icon.svg',
}, },
alternates: { alternates: {
canonical: '/', canonical: '/',
@@ -24,16 +24,16 @@ export const metadata: Metadata = {
}, },
}, },
openGraph: { openGraph: {
title: 'آرام لند - دستیار آرامش و مدیتیشن فارسی', title: 'آرام جان - دستیار آرامش و مدیتیشن فارسی',
description: 'اپلیکیشن آرام لند - همراه شما برای رسیدن به آرامش درونی با مدیتیشن‌های فارسی و تمرین‌های تنفسی', description: 'اپلیکیشن آرام جان - همراه شما برای رسیدن به آرامش درونی با مدیتیشن‌های فارسی و تمرین‌های تنفسی',
url: 'https://aramland.approagency.ir/', url: 'https://aramland.approagency.ir/',
siteName: 'آرام لند', siteName: 'آرام جان',
images: [ images: [
{ {
url: '/og-image.png', // Create this image (1200x630px) url: '/og-image.png', // Create this image (1200x630px)
width: 1200, width: 1200,
height: 630, height: 630,
alt: ' آرام لند - اپلیکیشن مدیتیشن و آرامش - تمرین تنفسی', alt: ' آرام جان - اپلیکیشن مدیتیشن و آرامش - تمرین تنفسی',
}, },
], ],
@@ -43,8 +43,8 @@ export const metadata: Metadata = {
twitter: { twitter: {
card: 'summary_large_image', card: 'summary_large_image',
title: 'آرام لند - تمرین تنفسی و مدیتیشن', title: 'آرام جان - تمرین تنفسی و مدیتیشن',
description: 'اپلیکیشن آرام لند - همراه شما برای رسیدن به آرامش درونی', description: 'اپلیکیشن آرام جان - همراه شما برای رسیدن به آرامش درونی',
images: ['/og-image.png'], images: ['/og-image.png'],
creator: '@aramland', // Replace with your Twitter handle creator: '@aramland', // Replace with your Twitter handle
}, },
@@ -78,10 +78,9 @@ export default function RootLayout({
name="google-site-verification" name="google-site-verification"
content="HIKgdf_siYdPxbJ2P1792opMprpxci_QjE-quk9kyi8" content="HIKgdf_siYdPxbJ2P1792opMprpxci_QjE-quk9kyi8"
/> />
{/* Favicon */} {/* Favicon — lotus mark (آرام جان) */}
<link rel="icon" href="/favicon.ico" sizes="any" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<link rel="icon" href="/icon?<generated>" type="image/<generated>" sizes="<generated>" /> <link rel="apple-touch-icon" href="/icon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
{/* Preload critical resources */} {/* Preload critical resources */}
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
@@ -99,8 +98,8 @@ export default function RootLayout({
"ratingValue": "4.5", "ratingValue": "4.5",
"ratingCount": "1000" "ratingCount": "1000"
}, },
"name": "آرام لند", "name": "آرام جان",
"description": "اپلیکیشن آرام لند - همراه شما برای رسیدن به آرامش درونی با مدیتیشن‌های فارسی و تمرین‌های تنفسی", "description": "اپلیکیشن آرام جان - همراه شما برای رسیدن به آرامش درونی با مدیتیشن‌های فارسی و تمرین‌های تنفسی",
"url": "https://aramland.approagency.ir/", "url": "https://aramland.approagency.ir/",
"applicationCategory": "HealthApplication", "applicationCategory": "HealthApplication",
"operatingSystem": "Android, iOS, Web", "operatingSystem": "Android, iOS, Web",
@@ -111,7 +110,7 @@ export default function RootLayout({
}, },
"author": { "author": {
"@type": "Organization", "@type": "Organization",
"name": "آرام لند" "name": "آرام جان"
} }
}) })
}} }}
BIN
View File
Binary file not shown.
+2 -1
View File
@@ -6,7 +6,8 @@
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "eslint" "lint": "eslint",
"deploy": "bash scripts/deploy.sh"
}, },
"dependencies": { "dependencies": {
"framer-motion": "^12.23.24", "framer-motion": "^12.23.24",
+12
View File
@@ -0,0 +1,12 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.approagency.meditation",
"sha256_cert_fingerprints": [
"7B:43:79:DE:71:EB:86:4B:60:FF:8D:CC:49:7C:D3:F7:91:7B:86:EB:30:B7:2C:1A:CD:98:44:AC:34:9E:E4:DA"
]
}
}
]
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Build the static site locally, then replace the server directory with it.
#
# - Builds on THIS machine (npm run build -> ./out).
# - Streams out/ over a single SSH connection (tar pipe) and swaps the
# contents of the target dir on the server. No rsync/sshpass required.
#
# Password handling:
# - If `sshpass` is installed AND DEPLOY_PASSWORD is set (env or .env.deploy),
# the deploy is fully unattended.
# - Otherwise SSH prompts for the password once (interactive).
#
# Config (override via env or .env.deploy):
set -euo pipefail
cd "$(git rev-parse --show-toplevel 2>/dev/null || dirname "$(dirname "$0")")"
# Optional local secrets file (gitignored).
[ -f .env.deploy ] && . ./.env.deploy
DEPLOY_HOST="${DEPLOY_HOST:-185.226.116.88}"
DEPLOY_USER="${DEPLOY_USER:-ubuntu}"
DEPLOY_PATH="${DEPLOY_PATH:-/var/www/aramland-front}"
DEPLOY_PASSWORD="${DEPLOY_PASSWORD:-}"
echo "▸ Building locally (clean)…"
# Remove caches so the production build never type-checks a stale `.next/dev`
# validator (tsconfig includes .next/dev/types) and never ships stale out/ files.
rm -rf .next out
npm run build
if [ ! -d out ]; then
echo "✗ Build did not produce ./out" >&2
exit 1
fi
# Pick the SSH command: unattended with sshpass, else interactive prompt.
ssh_cmd=(ssh -o StrictHostKeyChecking=no "$DEPLOY_USER@$DEPLOY_HOST")
if command -v sshpass >/dev/null 2>&1 && [ -n "$DEPLOY_PASSWORD" ]; then
ssh_cmd=(sshpass -p "$DEPLOY_PASSWORD" "${ssh_cmd[@]}")
else
echo " sshpass/password not available — SSH will prompt for the password."
fi
echo "▸ Uploading to $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH"
# Replace the directory contents (incl. dotfiles) then extract the new build.
tar -C out -czf - . | "${ssh_cmd[@]}" \
"set -e; mkdir -p '$DEPLOY_PATH'; find '$DEPLOY_PATH' -mindepth 1 -delete; tar -C '$DEPLOY_PATH' -xzf -"
echo "✓ Deployed to $DEPLOY_HOST:$DEPLOY_PATH"