Add LINE icon to website-creator skill

This commit is contained in:
Kunthawat Greethong
2026-03-22 11:43:55 +07:00
parent 1d2a5514d4
commit fe48c4c294
3 changed files with 324 additions and 231 deletions

View File

@@ -39,6 +39,7 @@ from urllib.parse import urlparse
# INTERACTIVE SETUP FUNCTIONS # INTERACTIVE SETUP FUNCTIONS
# ============================================================================ # ============================================================================
def ask_analytics_setup(): def ask_analytics_setup():
""" """
Interactive analytics setup workflow Interactive analytics setup workflow
@@ -51,41 +52,45 @@ def ask_analytics_setup():
print("=" * 60) print("=" * 60)
config = { config = {
'search_console': None, "search_console": None,
'analytics_type': None, # 'umami' or 'ga4' "analytics_type": None, # 'umami' or 'ga4'
'umami_auto_create': False, "umami_auto_create": False,
'umami_website_id': None, "umami_website_id": None,
'ga4_property_id': None, "ga4_property_id": None,
'ga4_credentials_path': None, "ga4_credentials_path": None,
'ga4_existing': False "ga4_existing": False,
} }
# Step 1: Google Search Console (for all websites) # Step 1: Google Search Console (for all websites)
print("\n1⃣ Google Search Console Setup") print("\n1⃣ Google Search Console Setup")
print(" GSC is recommended for all websites for SEO monitoring.") print(" GSC is recommended for all websites for SEO monitoring.")
gsc_choice = input("\n Do you want to setup Google Search Console? (y/n): ").strip().lower() gsc_choice = (
input("\n Do you want to setup Google Search Console? (y/n): ")
.strip()
.lower()
)
if gsc_choice == 'y': if gsc_choice == "y":
print("\n GSC Setup Options:") print("\n GSC Setup Options:")
print(" 1. I'll add it manually later (skip for now)") print(" 1. I'll add it manually later (skip for now)")
print(" 2. I have service account credentials file") print(" 2. I have service account credentials file")
gsc_method = input("\n Choose option (1-2): ").strip() gsc_method = input("\n Choose option (1-2): ").strip()
if gsc_method == '2': if gsc_method == "2":
gsc_path = input(" Enter path to GSC credentials file: ").strip() gsc_path = input(" Enter path to GSC credentials file: ").strip()
if os.path.exists(gsc_path): if os.path.exists(gsc_path):
config['search_console'] = { config["search_console"] = {
'credentials_path': gsc_path, "credentials_path": gsc_path,
'setup_later': False "setup_later": False,
} }
print(" ✓ GSC credentials loaded") print(" ✓ GSC credentials loaded")
else: else:
print(" ⚠ File not found, will setup later") print(" ⚠ File not found, will setup later")
config['search_console'] = {'setup_later': True} config["search_console"] = {"setup_later": True}
else: else:
config['search_console'] = {'setup_later': True} config["search_console"] = {"setup_later": True}
print(" ✓ Will setup later") print(" ✓ Will setup later")
else: else:
print(" ⏭️ Skipping GSC setup") print(" ⏭️ Skipping GSC setup")
@@ -104,66 +109,78 @@ def ask_analytics_setup():
analytics_choice = input("\n Choose analytics (1-2): ").strip() analytics_choice = input("\n Choose analytics (1-2): ").strip()
if analytics_choice == '1': if analytics_choice == "1":
# Umami setup # Umami setup
config['analytics_type'] = 'umami' config["analytics_type"] = "umami"
print("\n 📈 Umami Analytics Setup") print("\n 📈 Umami Analytics Setup")
# Check if Umami credentials are configured # Check if Umami credentials are configured
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv(os.path.join(os.path.dirname(__file__), '../../../.env'))
umami_url = os.getenv('UMAMI_URL', '') load_dotenv(os.path.join(os.path.dirname(__file__), "../../../.env"))
umami_username = os.getenv('UMAMI_USERNAME', '')
umami_password = os.getenv('UMAMI_PASSWORD', '') umami_url = os.getenv("UMAMI_URL", "")
umami_username = os.getenv("UMAMI_USERNAME", "")
umami_password = os.getenv("UMAMI_PASSWORD", "")
if umami_url and umami_username and umami_password: if umami_url and umami_username and umami_password:
print(" ✓ Umami credentials found in .env") print(" ✓ Umami credentials found in .env")
print(" ✓ Will auto-create Umami website for this project") print(" ✓ Will auto-create Umami website for this project")
config['umami_auto_create'] = True config["umami_auto_create"] = True
else: else:
print(" ⚠ Umami credentials not configured in .env") print(" ⚠ Umami credentials not configured in .env")
print(" ⏭️ Skipping Umami setup (can add manually later)") print(" ⏭️ Skipping Umami setup (can add manually later)")
elif analytics_choice == '2': elif analytics_choice == "2":
# GA4 setup # GA4 setup
config['analytics_type'] = 'ga4' config["analytics_type"] = "ga4"
print("\n 🔍 Google Analytics 4 Setup") print("\n 🔍 Google Analytics 4 Setup")
print(" 1. Create new GA4 property (auto-setup)") print(" 1. Create new GA4 property (auto-setup)")
print(" 2. Use existing GA4 property (manual setup)") print(" 2. Use existing GA4 property (manual setup)")
ga4_choice = input("\n Choose option (1-2): ").strip() ga4_choice = input("\n Choose option (1-2): ").strip()
if ga4_choice == '1': if ga4_choice == "1":
print("\n ⚠ Auto-creating GA4 properties requires API setup.") print("\n ⚠ Auto-creating GA4 properties requires API setup.")
print(" ⏭️ Will provide instructions for manual setup") print(" ⏭️ Will provide instructions for manual setup")
config['ga4_existing'] = False config["ga4_existing"] = False
else: else:
print("\n Please provide your existing GA4 details:") print("\n Please provide your existing GA4 details:")
# Check unified .env for GA4 credentials # Check unified .env for GA4 credentials
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv(os.path.join(os.path.dirname(__file__), '../../../.env'))
ga4_property_id = os.getenv('GA4_PROPERTY_ID', '') load_dotenv(os.path.join(os.path.dirname(__file__), "../../../.env"))
ga4_credentials_path = os.getenv('GA4_CREDENTIALS_PATH', '')
ga4_property_id = os.getenv("GA4_PROPERTY_ID", "")
ga4_credentials_path = os.getenv("GA4_CREDENTIALS_PATH", "")
if ga4_property_id: if ga4_property_id:
print(f" Found GA4 Property ID in .env: {ga4_property_id[:20]}...") print(f" Found GA4 Property ID in .env: {ga4_property_id[:20]}...")
use_global = input(" Use this for this project? (y/n): ").strip().lower() use_global = (
input(" Use this for this project? (y/n): ").strip().lower()
)
if use_global == 'y': if use_global == "y":
config['ga4_property_id'] = ga4_property_id config["ga4_property_id"] = ga4_property_id
config['ga4_credentials_path'] = ga4_credentials_path config["ga4_credentials_path"] = ga4_credentials_path
print(" ✓ Using global GA4 credentials") print(" ✓ Using global GA4 credentials")
else: else:
config['ga4_property_id'] = input(" Enter GA4 Property ID: ").strip() config["ga4_property_id"] = input(
config['ga4_credentials_path'] = input(" Enter GA4 credentials file path: ").strip() " Enter GA4 Property ID: "
).strip()
config["ga4_credentials_path"] = input(
" Enter GA4 credentials file path: "
).strip()
else: else:
config['ga4_property_id'] = input(" Enter GA4 Property ID (G-XXXXXXXXXX): ").strip() config["ga4_property_id"] = input(
config['ga4_credentials_path'] = input(" Enter GA4 credentials file path: ").strip() " Enter GA4 Property ID (G-XXXXXXXXXX): "
).strip()
config["ga4_credentials_path"] = input(
" Enter GA4 credentials file path: "
).strip()
config['ga4_existing'] = True config["ga4_existing"] = True
else: else:
print(" ⏭️ Skipping analytics setup") print(" ⏭️ Skipping analytics setup")
@@ -237,49 +254,64 @@ PACKAGE_JSON_TEMPLATE = """{{
# MAIN FUNCTION # MAIN FUNCTION
# ============================================================================ # ============================================================================
def main(): def main():
"""Main entry point.""" """Main entry point."""
parser = argparse.ArgumentParser(description='Create PDPA-compliant Astro website') parser = argparse.ArgumentParser(description="Create PDPA-compliant Astro website")
parser.add_argument('--name', required=True, help='Website name') parser.add_argument("--name", required=True, help="Website name")
parser.add_argument('--type', default='corporate', parser.add_argument(
choices=['corporate', 'portfolio', 'landing', 'blog', 'ecommerce'], "--type",
help='Website type') default="corporate",
parser.add_argument('--languages', default='th,en', choices=["corporate", "portfolio", "landing", "blog", "ecommerce"],
help='Languages (comma-separated): th, en') help="Website type",
parser.add_argument('--primary-color', default='#2563eb', )
help='Primary color (hex)') parser.add_argument(
parser.add_argument('--secondary-color', default='#1e40af', "--languages", default="th,en", help="Languages (comma-separated): th, en"
help='Secondary color (hex)') )
parser.add_argument('--features', default='blog,contact', parser.add_argument(
help='Features (comma-separated): blog, products, contact, portfolio') "--primary-color", default="#2563eb", help="Primary color (hex)"
parser.add_argument('--umami-id', default='', )
help='Umami Website ID') parser.add_argument(
parser.add_argument('--umami-domain', default='analytics.example.com', "--secondary-color", default="#1e40af", help="Secondary color (hex)"
help='Umami domain') )
parser.add_argument('--output', '-o', default='.', parser.add_argument(
help='Output directory') "--features",
parser.add_argument('--no-interactive', action='store_true', default="blog,contact",
help='Skip interactive setup (use defaults)') help="Features (comma-separated): blog, products, contact, portfolio",
)
parser.add_argument("--umami-id", default="", help="Umami Website ID")
parser.add_argument(
"--umami-domain", default="analytics.example.com", help="Umami domain"
)
parser.add_argument("--output", "-o", default=".", help="Output directory")
parser.add_argument(
"--no-interactive",
action="store_true",
help="Skip interactive setup (use defaults)",
)
args = parser.parse_args() args = parser.parse_args()
# Auto-generate admin password from project folder name # Auto-generate admin password from project folder name
args.admin_password = Path(args.output).name.replace(' ', '').lower() args.admin_password = Path(args.output).name.replace(" ", "").lower()
# Load unified credentials # Load unified credentials
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv(os.path.join(os.path.dirname(__file__), '../../../.env'))
load_dotenv(os.path.join(os.path.dirname(__file__), "../../../.env"))
# Get Umami credentials for auto-setup # Get Umami credentials for auto-setup
args.umami_url = os.getenv('UMAMI_URL', '') args.umami_url = os.getenv("UMAMI_URL", "")
args.umami_username = os.getenv('UMAMI_USERNAME', '') args.umami_username = os.getenv("UMAMI_USERNAME", "")
args.umami_password = os.getenv('UMAMI_PASSWORD', '') args.umami_password = os.getenv("UMAMI_PASSWORD", "")
args.auto_setup_umami = bool(args.umami_url and args.umami_username and args.umami_password) args.auto_setup_umami = bool(
args.umami_url and args.umami_username and args.umami_password
)
languages = [lang.strip() for lang in args.languages.split(',')] languages = [lang.strip() for lang in args.languages.split(",")]
default_locale = 'en' if 'en' in languages else languages[0] default_locale = "en" if "en" in languages else languages[0]
features = [f.strip() for f in args.features.split(',')] features = [f.strip() for f in args.features.split(",")]
print(f"Creating website: {args.name}") print(f"Creating website: {args.name}")
print(f"Type: {args.type}") print(f"Type: {args.type}")
@@ -301,24 +333,29 @@ def main():
# Auto-setup Umami if credentials provided # Auto-setup Umami if credentials provided
umami_website_id = args.umami_id umami_website_id = args.umami_id
if args.auto_setup_umami and (not analytics_config or analytics_config.get('analytics_type') == 'umami'): if args.auto_setup_umami and (
not analytics_config or analytics_config.get("analytics_type") == "umami"
):
print("\n📈 Setting up Umami Analytics...") print("\n📈 Setting up Umami Analytics...")
try: try:
from umami_integration import setup_umami_for_website from umami_integration import setup_umami_for_website
website_domain = args.name.lower().replace(' ', '-') + '.moreminimore.com'
website_domain = args.name.lower().replace(" ", "-") + ".moreminimore.com"
success, result = setup_umami_for_website( success, result = setup_umami_for_website(
args.umami_url, args.umami_url,
args.umami_username, args.umami_username,
args.umami_password, args.umami_password,
args.name, args.name,
website_domain, website_domain,
args.output args.output,
) )
if success: if success:
umami_website_id = result['website_id'] umami_website_id = result["website_id"]
print(f" ✓ Umami website created: {umami_website_id}") print(f" ✓ Umami website created: {umami_website_id}")
else: else:
print(f" ⚠ Umami setup skipped: {result.get('error', 'Unknown error')}") print(
f" ⚠ Umami setup skipped: {result.get('error', 'Unknown error')}"
)
except Exception as e: except Exception as e:
print(f" ⚠ Umami setup failed: {e}") print(f" ⚠ Umami setup failed: {e}")
print(" Continuing without Umami...") print(" Continuing without Umami...")
@@ -326,11 +363,11 @@ def main():
print(f"\n✅ Website created successfully at: {args.output}") print(f"\n✅ Website created successfully at: {args.output}")
# Update .env with Umami ID if auto-setup # Update .env with Umami ID if auto-setup
env_file = os.path.join(args.output, '.env') env_file = os.path.join(args.output, ".env")
if os.path.exists(env_file) and umami_website_id: if os.path.exists(env_file) and umami_website_id:
with open(env_file, 'a', encoding='utf-8') as f: with open(env_file, "a", encoding="utf-8") as f:
f.write(f'\n# Umami Analytics (auto-configured)\n') f.write(f"\n# Umami Analytics (auto-configured)\n")
f.write(f'UMAMI_WEBSITE_ID={umami_website_id}\n') f.write(f"UMAMI_WEBSITE_ID={umami_website_id}\n")
print(f" ✓ Umami ID added to .env") print(f" ✓ Umami ID added to .env")
print("\nNext steps:") print("\nNext steps:")
@@ -354,12 +391,18 @@ def main():
print("To sync later, run the sync command manually.") print("To sync later, run the sync command manually.")
return return
sync_choice = input("Do you want to sync to Gitea and deploy to Easypanel? (y/n): ").strip().lower() sync_choice = (
input("Do you want to sync to Gitea and deploy to Easypanel? (y/n): ")
.strip()
.lower()
)
if sync_choice != 'y': if sync_choice != "y":
print("") print("")
print("✅ Done! Website is ready at:", args.output) print("✅ Done! Website is ready at:", args.output)
print("To sync later, run this script again or use gitea-sync/easypanel-deploy skills.") print(
"To sync later, run this script again or use gitea-sync/easypanel-deploy skills."
)
return return
print("") print("")
@@ -399,30 +442,38 @@ def main():
def save_analytics_config(output_path: str, config: dict): def save_analytics_config(output_path: str, config: dict):
"""Save analytics configuration to project context""" """Save analytics configuration to project context"""
context_dir = os.path.join(output_path, 'context') context_dir = os.path.join(output_path, "context")
os.makedirs(context_dir, exist_ok=True) os.makedirs(context_dir, exist_ok=True)
# Save data-services.json # Save data-services.json
data_services = { data_services = {
'ga4': { "ga4": {
'enabled': config.get('analytics_type') == 'ga4', "enabled": config.get("analytics_type") == "ga4",
'property_id': config.get('ga4_property_id', ''), "property_id": config.get("ga4_property_id", ""),
'credentials_path': config.get('ga4_credentials_path', '') "credentials_path": config.get("ga4_credentials_path", ""),
} if config.get('analytics_type') == 'ga4' else {'enabled': False}, }
'gsc': { if config.get("analytics_type") == "ga4"
'enabled': config.get('search_console') is not None, else {"enabled": False},
'site_url': '', "gsc": {
'credentials_path': config.get('search_console', {}).get('credentials_path', '') "enabled": config.get("search_console") is not None,
"site_url": "",
"credentials_path": config.get("search_console", {}).get(
"credentials_path", ""
),
}, },
'umami': { "umami": {
'enabled': config.get('analytics_type') == 'umami', "enabled": config.get("analytics_type") == "umami",
'api_url': os.getenv('UMAMI_URL', ''), "api_url": os.getenv("UMAMI_URL", ""),
'website_id': config.get('umami_website_id', '') "website_id": config.get("umami_website_id", ""),
} if config.get('analytics_type') == 'umami' else {'enabled': False}, }
'dataforseo': {'enabled': False} if config.get("analytics_type") == "umami"
else {"enabled": False},
"dataforseo": {"enabled": False},
} }
with open(os.path.join(context_dir, 'data-services.json'), 'w', encoding='utf-8') as f: with open(
os.path.join(context_dir, "data-services.json"), "w", encoding="utf-8"
) as f:
json.dump(data_services, f, indent=2) json.dump(data_services, f, indent=2)
print(f" ✓ Analytics config saved to context/data-services.json") print(f" ✓ Analytics config saved to context/data-services.json")
@@ -432,31 +483,33 @@ def save_analytics_config(output_path: str, config: dict):
# PROJECT CREATION FUNCTIONS # PROJECT CREATION FUNCTIONS
# ============================================================================ # ============================================================================
def create_project(args, languages, default_locale, features): def create_project(args, languages, default_locale, features):
"""Create the Astro project structure with templates.""" """Create the Astro project structure with templates."""
output_path = Path(args.output) output_path = Path(args.output)
project_name = args.name.lower().replace(' ', '-') project_name = args.name.lower().replace(" ", "-")
site_url = f"https://{project_name}.moreminimore.com" site_url = f"https://{project_name}.moreminimore.com"
# Get template directory # Get template directory
script_dir = Path(__file__).parent script_dir = Path(__file__).parent
template_dir = script_dir / 'templates' template_dir = script_dir / "templates"
print("\n📁 Creating project structure...") print("\n📁 Creating project structure...")
# Create directories # Create directories
dirs = [ dirs = [
output_path / 'public' / 'images', output_path / "public" / "images",
output_path / 'src' / 'components' / 'common', output_path / "public" / "images" / "icons",
output_path / 'src' / 'components' / 'consent', output_path / "src" / "components" / "common",
output_path / 'src' / 'components' / 'ui', output_path / "src" / "components" / "consent",
output_path / 'src' / 'layouts', output_path / "src" / "components" / "ui",
output_path / 'src' / 'pages', output_path / "src" / "layouts",
output_path / 'src' / 'pages' / default_locale, output_path / "src" / "pages",
output_path / 'src' / 'styles', output_path / "src" / "pages" / default_locale,
output_path / 'src' / 'content' / 'blog', output_path / "src" / "styles",
output_path / 'src' / 'lib', output_path / "src" / "content" / "blog",
output_path / 'db', output_path / "src" / "lib",
output_path / "db",
] ]
for d in dirs: for d in dirs:
@@ -469,48 +522,68 @@ def create_project(args, languages, default_locale, features):
print(" 📦 Copying templates with IDs...") print(" 📦 Copying templates with IDs...")
# Copy layouts # Copy layouts
layout_src = template_dir / 'layouts' / 'BaseLayout.astro' layout_src = template_dir / "layouts" / "BaseLayout.astro"
if layout_src.exists(): if layout_src.exists():
content = layout_src.read_text(encoding='utf-8') content = layout_src.read_text(encoding="utf-8")
content = content.replace("const siteName = 'Website Name'", f"const siteName = '{args.name}'") content = content.replace(
content = content.replace("const siteUrl = 'https://example.com'", f"const siteUrl = '{site_url}'") "const siteName = 'Website Name'", f"const siteName = '{args.name}'"
(output_path / 'src' / 'layouts' / 'BaseLayout.astro').write_text(content, encoding='utf-8') )
content = content.replace(
"const siteUrl = 'https://example.com'", f"const siteUrl = '{site_url}'"
)
(output_path / "src" / "layouts" / "BaseLayout.astro").write_text(
content, encoding="utf-8"
)
# Copy Header # Copy Header
header_src = template_dir / 'components' / 'common' / 'Header.astro' header_src = template_dir / "components" / "common" / "Header.astro"
if header_src.exists(): if header_src.exists():
shutil.copy(header_src, output_path / 'src' / 'components' / 'common' / 'Header.astro') shutil.copy(
header_src,
output_path / "src" / "components" / "common" / "Header.astro",
)
# Copy Footer # Copy Footer
footer_src = template_dir / 'components' / 'common' / 'Footer.astro' footer_src = template_dir / "components" / "common" / "Footer.astro"
if footer_src.exists(): if footer_src.exists():
shutil.copy(footer_src, output_path / 'src' / 'components' / 'common' / 'Footer.astro') shutil.copy(
footer_src,
output_path / "src" / "components" / "common" / "Footer.astro",
)
# Copy page templates # Copy page templates
page_src = template_dir / 'pages' / 'index.astro' page_src = template_dir / "pages" / "index.astro"
if page_src.exists(): if page_src.exists():
shutil.copy(page_src, output_path / 'src' / 'pages' / default_locale / 'index.astro') shutil.copy(
page_src, output_path / "src" / "pages" / default_locale / "index.astro"
)
# Copy styles # Copy styles
style_src = template_dir / 'styles' / 'global.css' style_src = template_dir / "styles" / "global.css"
if style_src.exists(): if style_src.exists():
shutil.copy(style_src, output_path / 'src' / 'styles' / 'global.css') shutil.copy(style_src, output_path / "src" / "styles" / "global.css")
# Copy LINE icon
line_icon_src = template_dir / "icons" / "line.svg"
if line_icon_src.exists():
icons_dir = output_path / "public" / "images" / "icons"
icons_dir.mkdir(parents=True, exist_ok=True)
shutil.copy(line_icon_src, icons_dir / "line.svg")
print(" ✓ LINE icon copied")
print(" ✓ Templates copied") print(" ✓ Templates copied")
# Create astro.config.mjs # Create astro.config.mjs
locales_str = ', '.join([f"'{lang}'" for lang in languages]) locales_str = ", ".join([f"'{lang}'" for lang in languages])
astro_config = ASTRO_CONFIG_TEMPLATE.format( astro_config = ASTRO_CONFIG_TEMPLATE.format(
site_url=site_url, site_url=site_url, locales=locales_str, default_locale=default_locale
locales=locales_str,
default_locale=default_locale
) )
(output_path / 'astro.config.mjs').write_text(astro_config, encoding='utf-8') (output_path / "astro.config.mjs").write_text(astro_config, encoding="utf-8")
print(" ✓ astro.config.mjs created") print(" ✓ astro.config.mjs created")
# Create package.json # Create package.json
package_json = PACKAGE_JSON_TEMPLATE.format(name=project_name) package_json = PACKAGE_JSON_TEMPLATE.format(name=project_name)
(output_path / 'package.json').write_text(package_json, encoding='utf-8') (output_path / "package.json").write_text(package_json, encoding="utf-8")
print(" ✓ package.json created") print(" ✓ package.json created")
# Create tsconfig.json # Create tsconfig.json
@@ -524,7 +597,7 @@ def create_project(args, languages, default_locale, features):
} }
} }
""" """
(output_path / 'tsconfig.json').write_text(tsconfig, encoding='utf-8') (output_path / "tsconfig.json").write_text(tsconfig, encoding="utf-8")
# Create env file # Create env file
env_content = f"""# Website Configuration env_content = f"""# Website Configuration
@@ -535,11 +608,11 @@ SITE_URL={site_url}
# UMAMI_WEBSITE_ID= # UMAMI_WEBSITE_ID=
# UMAMI_URL= # UMAMI_URL=
""" """
(output_path / '.env').write_text(env_content, encoding='utf-8') (output_path / ".env").write_text(env_content, encoding="utf-8")
print(" ✓ Configuration files created") print(" ✓ Configuration files created")
# Create basic index page if no template # Create basic index page if no template
if not (output_path / 'src' / 'pages' / default_locale / 'index.astro').exists(): if not (output_path / "src" / "pages" / default_locale / "index.astro").exists():
index_content = f"""--- index_content = f"""---
import BaseLayout from '../layouts/BaseLayout.astro'; import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/common/Header.astro'; import Header from '../components/common/Header.astro';
@@ -557,7 +630,9 @@ import Footer from '../components/common/Footer.astro';
<Footer /> <Footer />
</BaseLayout> </BaseLayout>
""" """
(output_path / 'src' / 'pages' / default_locale / 'index.astro').write_text(index_content, encoding='utf-8') (output_path / "src" / "pages" / default_locale / "index.astro").write_text(
index_content, encoding="utf-8"
)
print(" ✓ Basic pages created") print(" ✓ Basic pages created")
@@ -580,7 +655,7 @@ RUN npm run build
EXPOSE 80 EXPOSE 80
CMD ["npm", "run", "preview"] CMD ["npm", "run", "preview"]
""" """
(output_path / 'Dockerfile').write_text(dockerfile, encoding='utf-8') (output_path / "Dockerfile").write_text(dockerfile, encoding="utf-8")
print(" ✓ Dockerfile created") print(" ✓ Dockerfile created")
# Create .gitignore # Create .gitignore
@@ -605,7 +680,7 @@ dist/
.DS_Store .DS_Store
Thumbs.db Thumbs.db
""" """
(output_path / '.gitignore').write_text(gitignore, encoding='utf-8') (output_path / ".gitignore").write_text(gitignore, encoding="utf-8")
print(" ✓ .gitignore created") print(" ✓ .gitignore created")
return output_path return output_path
@@ -615,7 +690,7 @@ def sync_to_gitea(output_path: str, repo_name: str) -> str:
"""Sync project to Gitea repository.""" """Sync project to Gitea repository."""
try: try:
# Import gitea sync functionality # Import gitea sync functionality
sys.path.insert(0, str(Path(__file__).parent.parent / 'gitea-sync' / 'scripts')) sys.path.insert(0, str(Path(__file__).parent.parent / "gitea-sync" / "scripts"))
from sync import sync_repo from sync import sync_repo
# Use the gitea-sync script # Use the gitea-sync script
@@ -623,10 +698,12 @@ def sync_to_gitea(output_path: str, repo_name: str) -> str:
repo_name=repo_name, repo_name=repo_name,
repo_path=output_path, repo_path=output_path,
description=f"Website: {repo_name}", description=f"Website: {repo_name}",
auto_push=True auto_push=True,
)
if result.get("success"):
return result.get(
"url", f"https://git.moreminimore.com/user/{repo_name}.git"
) )
if result.get('success'):
return result.get('url', f"https://git.moreminimore.com/user/{repo_name}.git")
else: else:
print(f" ⚠ Gitea sync failed: {result.get('error')}") print(f" ⚠ Gitea sync failed: {result.get('error')}")
return f"https://git.moreminimore.com/user/{repo_name}.git" return f"https://git.moreminimore.com/user/{repo_name}.git"
@@ -641,20 +718,22 @@ def deploy_to_easypanel(output_path: str, project_name: str, git_url: str) -> st
"""Deploy project to Easypanel.""" """Deploy project to Easypanel."""
try: try:
# Import easypanel deploy functionality # Import easypanel deploy functionality
sys.path.insert(0, str(Path(__file__).parent.parent / 'easypanel-deploy' / 'scripts')) sys.path.insert(
0, str(Path(__file__).parent.parent / "easypanel-deploy" / "scripts")
)
from deploy import ( from deploy import (
get_session_token, get_session_token,
create_service, create_service,
update_git_source, update_git_source,
update_build_type, update_build_type,
deploy_service, deploy_service,
load_env load_env,
) )
# Load credentials # Load credentials
env = load_env() env = load_env()
username = env.get('EASYPANEL_USERNAME', '') username = env.get("EASYPANEL_USERNAME", "")
password = env.get('EASYPANEL_PASSWORD', '') password = env.get("EASYPANEL_PASSWORD", "")
if not username or not password: if not username or not password:
print(" ⚠ Easypanel credentials not found") print(" ⚠ Easypanel credentials not found")
@@ -668,16 +747,16 @@ def deploy_to_easypanel(output_path: str, project_name: str, git_url: str) -> st
return f"https://{project_name}.moreminimore.com" return f"https://{project_name}.moreminimore.com"
# Create service # Create service
create_service(project_name, 'web', token) create_service(project_name, "web", token)
# Update git source # Update git source
update_git_source(project_name, 'web', git_url, 'main', token) update_git_source(project_name, "web", git_url, "main", token)
# Set build type to dockerfile # Set build type to dockerfile
update_build_type(project_name, 'web', token, 'dockerfile') update_build_type(project_name, "web", token, "dockerfile")
# Deploy # Deploy
deploy_service(project_name, 'web', token) deploy_service(project_name, "web", token)
return f"https://{project_name}.moreminimore.com" return f"https://{project_name}.moreminimore.com"
except Exception as e: except Exception as e:
@@ -693,5 +772,5 @@ def monitor_deployment(project_name: str):
print(" Check status at: https://panelwebsite.moreminimore.com") print(" Check status at: https://panelwebsite.moreminimore.com")
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View File

@@ -22,9 +22,9 @@ const legalLinks = [
]; ];
const socialLinks = [ const socialLinks = [
{ name: 'Facebook', href: 'https://facebook.com', icon: 'facebook' }, { name: 'Facebook', href: 'https://facebook.com', icon: 'facebook', svg: '' },
{ name: 'Line', href: 'https://line.me', icon: 'line' }, { name: 'Line', href: 'https://line.me', icon: 'line', svg: 'line' },
{ name: 'YouTube', href: 'https://youtube.com', icon: 'youtube' }, { name: 'YouTube', href: 'https://youtube.com', icon: 'youtube', svg: '' },
]; ];
--- ---
@@ -45,9 +45,13 @@ const socialLinks = [
<!-- Social Links --> <!-- Social Links -->
<div id="footer-social"> <div id="footer-social">
<div id="social-links-container" class="flex space-x-4"> <div id="social-links-container" class="flex space-x-4">
{socialLinks.map((social, index) => ( {socialLinks.map((social) => (
<a id={`social-${social.icon}`} href={social.href} target="_blank" rel="noopener noreferrer" class="w-10 h-10 bg-secondary-800 rounded-full flex items-center justify-center hover:bg-primary-600 transition-colors" aria-label={social.name}> <a id={`social-${social.icon}`} href={social.href} target="_blank" rel="noopener noreferrer" class="w-10 h-10 bg-secondary-800 rounded-full flex items-center justify-center hover:bg-primary-600 transition-colors" aria-label={social.name}>
{social.svg === 'line' ? (
<img src="/images/icons/line.svg" alt="LINE" class="w-5 h-5" />
) : (
<span class="text-sm font-medium">{social.name[0]}</span> <span class="text-sm font-medium">{social.name[0]}</span>
)}
</a> </a>
))} ))}
</div> </div>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 36 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-6,-6)">
<path d="M12.5,42L35.5,42C39.09,42 42,39.09 42,35.5L42,12.5C42,8.91 39.09,6 35.5,6L12.5,6C8.91,6 6,8.91 6,12.5L6,35.5C6,39.09 8.91,42 12.5,42Z" style="fill:rgb(0,195,0);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,-6,-6)">
<path d="M37.113,22.417C37.113,16.552 31.233,11.78 24.006,11.78C16.779,11.78 10.898,16.552 10.898,22.417C10.898,27.675 15.561,32.079 21.86,32.912C22.287,33.004 22.868,33.194 23.015,33.558C23.147,33.889 23.101,34.408 23.057,34.743C23.057,34.743 22.904,35.668 22.87,35.865C22.813,36.196 22.607,37.161 24.005,36.572C25.404,35.983 31.553,32.127 34.303,28.961L34.302,28.961C36.203,26.879 37.113,24.764 37.113,22.417ZM18.875,25.907L16.271,25.907C15.892,25.907 15.584,25.599 15.584,25.219L15.584,20.01C15.584,19.631 15.892,19.323 16.271,19.323C16.65,19.323 16.958,19.631 16.958,20.01L16.958,24.531L18.875,24.531C19.254,24.531 19.562,24.839 19.562,25.218C19.562,25.598 19.254,25.907 18.875,25.907ZM21.568,25.219C21.568,25.598 21.26,25.907 20.881,25.907C20.502,25.907 20.194,25.599 20.194,25.219L20.194,20.01C20.194,19.631 20.502,19.323 20.881,19.323C21.26,19.323 21.568,19.631 21.568,20.01L21.568,25.219ZM27.838,25.219C27.838,25.516 27.65,25.778 27.368,25.871C27.297,25.895 27.223,25.907 27.15,25.907C26.935,25.907 26.73,25.804 26.601,25.632L23.932,21.997L23.932,25.219C23.932,25.598 23.624,25.907 23.244,25.907C22.865,25.907 22.556,25.599 22.556,25.219L22.556,20.01C22.556,19.714 22.745,19.452 23.026,19.358C23.097,19.334 23.17,19.323 23.244,19.323C23.458,19.323 23.664,19.426 23.793,19.598L26.463,23.233L26.463,20.01C26.463,19.631 26.772,19.323 27.151,19.323C27.53,19.323 27.838,19.631 27.838,20.01L27.838,25.219ZM32.052,21.927C32.431,21.927 32.74,22.235 32.74,22.615C32.74,22.994 32.432,23.302 32.052,23.302L30.135,23.302L30.135,24.532L32.052,24.532C32.431,24.532 32.74,24.84 32.74,25.219C32.74,25.598 32.431,25.907 32.052,25.907L29.448,25.907C29.07,25.907 28.761,25.599 28.761,25.219L28.761,20.011C28.761,19.632 29.069,19.324 29.448,19.324L32.052,19.324C32.431,19.324 32.74,19.632 32.74,20.011C32.74,20.39 32.432,20.698 32.052,20.698L30.135,20.698L30.135,21.928L32.052,21.928L32.052,21.927Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB