Merge branch 'main' into feat/homepage-icons
This commit is contained in:
commit
aa4f6eeb3f
57
.github/workflows/deploy-xcontrol-panel.yml
vendored
Normal file
57
.github/workflows/deploy-xcontrol-panel.yml
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
name: Deploy Xcontrol-panel to Cloudflare Pages
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'ui/nextjs/**'
|
||||
- '.github/workflows/deploy-xcontrol-panel.yml'
|
||||
workflow_dispatch:
|
||||
paths:
|
||||
- 'ui/nextjs/**'
|
||||
- '.github/workflows/deploy-xcontrol-panel.yml'
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ui/nextjs
|
||||
|
||||
steps:
|
||||
- name: 📦 Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🧰 Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 📥 Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: 🛠️ Build static site
|
||||
run: yarn build # next build (output: 'export' 会自动产出 ./out)
|
||||
|
||||
- name: 🚀 Deploy to Cloudflare Pages
|
||||
uses: cloudflare/pages-action@v1
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
projectName: xcontrol-panel # 和 Pages 控制台里看到的项目名保持一致
|
||||
directory: ui/nextjs/out # 指定到子目录 out
|
||||
wranglerVersion: '4' # 升级到 Wrangler v4,避免 v2 大量警告
|
||||
|
||||
# - name: 📤 Deploy to VPS via rsync
|
||||
# env:
|
||||
# RSYNC_SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
# VPS_HOST: ${{ secrets.VPS_HOST }}
|
||||
# run: |
|
||||
# mkdir -p ~/.ssh
|
||||
# echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
|
||||
# chmod 600 ~/.ssh/id_rsa
|
||||
# ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
|
||||
# rsync -av out/ root@"$VPS_HOST":/mnt/data/website/ --delete
|
||||
|
||||
- name: ✅ Confirm deployment complete
|
||||
run: echo "✅ Homepage deployed to Cloudflare & VPS Site."
|
||||
@ -2,6 +2,8 @@ import Hero from '@components/Hero'
|
||||
import Features from '@components/Features'
|
||||
import OpenSource from '@components/OpenSource'
|
||||
import DownloadSection from '@components/DownloadSection'
|
||||
import Terms from '@components/Terms'
|
||||
import Contact from '@components/Contact'
|
||||
import Footer from '@components/Footer'
|
||||
import NavBar from '@components/NavBar'
|
||||
|
||||
@ -14,6 +16,8 @@ export default function Page() {
|
||||
<Features />
|
||||
<OpenSource />
|
||||
<DownloadSection />
|
||||
<Terms />
|
||||
<Contact />
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
|
||||
17
ui/homepage/components/Contact.tsx
Normal file
17
ui/homepage/components/Contact.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
'use client'
|
||||
import { useLanguage } from '../i18n/LanguageProvider'
|
||||
import { translations } from '../i18n/translations'
|
||||
|
||||
export default function Contact() {
|
||||
const { language } = useLanguage()
|
||||
const t = translations[language]
|
||||
|
||||
return (
|
||||
<section id="contact" className="py-20 bg-gray-900 text-white">
|
||||
<div className="max-w-3xl mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold mb-6">{t.contactTitle}</h2>
|
||||
<p className="text-xl">manbuzhe2008@gmail.com</p>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@ -1,9 +1,13 @@
|
||||
'use client'
|
||||
import { useState } from 'react'
|
||||
import LanguageToggle from './LanguageToggle'
|
||||
import { useLanguage } from '../i18n/LanguageProvider'
|
||||
import { translations } from '../i18n/translations'
|
||||
|
||||
export default function NavBar() {
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const { language } = useLanguage()
|
||||
const nav = translations[language].nav
|
||||
|
||||
return (
|
||||
<nav className="fixed top-0 w-full z-50 bg-black/30 backdrop-blur border-b border-white/10">
|
||||
@ -15,10 +19,10 @@ export default function NavBar() {
|
||||
|
||||
{/* 桌面导航 */}
|
||||
<div className="hidden md:flex items-center gap-6 text-sm text-white">
|
||||
<a href="#features" className="hover:text-purple-300">Features</a>
|
||||
<a href="#open-sources" className="hover:text-purple-300">Open Source</a>
|
||||
<a href="#download" className="hover:text-purple-300">Download</a>
|
||||
<a href="#contact" className="hover:text-purple-300">Contact</a>
|
||||
<a href="#features" className="hover:text-purple-300">{nav.features}</a>
|
||||
<a href="#open-sources" className="hover:text-purple-300">{nav.openSource}</a>
|
||||
<a href="#download" className="hover:text-purple-300">{nav.download}</a>
|
||||
<a href="#contact" className="hover:text-purple-300">{nav.contact}</a>
|
||||
<LanguageToggle />
|
||||
</div>
|
||||
|
||||
@ -54,28 +58,28 @@ export default function NavBar() {
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
Features
|
||||
{nav.features}
|
||||
</a>
|
||||
<a
|
||||
href="#open-sources"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
Open Source
|
||||
{nav.openSource}
|
||||
</a>
|
||||
<a
|
||||
href="#download"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
Download
|
||||
{nav.download}
|
||||
</a>
|
||||
<a
|
||||
href="#contact"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
Contact
|
||||
{nav.contact}
|
||||
</a>
|
||||
<div className="pt-2">
|
||||
<LanguageToggle />
|
||||
|
||||
23
ui/homepage/components/Terms.tsx
Normal file
23
ui/homepage/components/Terms.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
'use client'
|
||||
import { useLanguage } from '../i18n/LanguageProvider'
|
||||
import { translations } from '../i18n/translations'
|
||||
|
||||
export default function Terms() {
|
||||
const { language } = useLanguage()
|
||||
const t = translations[language]
|
||||
|
||||
return (
|
||||
<section id="terms" className="py-20 bg-gray-950 text-white">
|
||||
<div className="max-w-3xl mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-6">{t.termsTitle}</h2>
|
||||
<ul className="list-disc pl-5 space-y-2">
|
||||
{t.termsPoints.map((p, idx) => (
|
||||
<li key={idx} className="text-gray-300">
|
||||
{p}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@ -11,6 +11,15 @@ export type Translation = {
|
||||
downloadTitle: string
|
||||
downloadSubtitle: string
|
||||
footerLinks: [string, string, string]
|
||||
nav: {
|
||||
features: string
|
||||
openSource: string
|
||||
download: string
|
||||
contact: string
|
||||
}
|
||||
termsTitle: string
|
||||
termsPoints: string[]
|
||||
contactTitle: string
|
||||
}
|
||||
|
||||
export const translations: Record<'en' | 'zh', Translation> = {
|
||||
@ -27,6 +36,20 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
downloadTitle: 'Download',
|
||||
downloadSubtitle: 'Select your platform',
|
||||
footerLinks: ['Privacy Policy', 'Terms of Service', 'Contact Us'],
|
||||
nav: {
|
||||
features: 'Features',
|
||||
openSource: 'Open Source',
|
||||
download: 'Download',
|
||||
contact: 'Contact',
|
||||
},
|
||||
termsTitle: 'Terms of Service',
|
||||
termsPoints: [
|
||||
'A free, open-source version for self-hosting on Windows, Linux, and macOS',
|
||||
'Affordable 1-on-1 consulting for technical setup',
|
||||
'A premium plan with cloud sync, mobile support, and device linking',
|
||||
'A future SaaS version for users who want one-click deployment with no setup required',
|
||||
],
|
||||
contactTitle: 'Contact Us',
|
||||
},
|
||||
zh: {
|
||||
hero: {
|
||||
@ -41,5 +64,19 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
downloadTitle: '下载',
|
||||
downloadSubtitle: '选择适合的平台',
|
||||
footerLinks: ['隐私政策', '服务条款', '联系我们'],
|
||||
nav: {
|
||||
features: '功能特性',
|
||||
openSource: '开源项目',
|
||||
download: '下载',
|
||||
contact: '联系我们',
|
||||
},
|
||||
termsTitle: '服务条款',
|
||||
termsPoints: [
|
||||
'提供在 Windows、Linux 和 macOS 上可自托管的免费开源版本',
|
||||
'提供经济实惠的 1 对 1 技术部署咨询服务',
|
||||
'提供带云同步、移动端支持和设备绑定的高级版计划',
|
||||
'未来将推出无需设置、一键部署的 SaaS 版本',
|
||||
],
|
||||
contactTitle: '联系我们',
|
||||
},
|
||||
}
|
||||
|
||||
@ -522,10 +522,10 @@ lru-cache@^10.2.0:
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
|
||||
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
|
||||
|
||||
lucide-react@^0.517.0:
|
||||
version "0.517.0"
|
||||
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.517.0.tgz#195ed8bd18e72462b012ba4e36d4474e480ff44c"
|
||||
integrity sha512-TQCwwbwIuVG6SSutUC2Ol6PRXcuZndqoVAnDa7S7xb/RWPaiKTvLwX7byUKeh0pUgvtFh0NZZwFIDuMSeB7Iwg==
|
||||
lucide-react@^0.292.0:
|
||||
version "0.292.0"
|
||||
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.292.0.tgz#c8a06b2ccd8a348a88669def3c0291c035de2884"
|
||||
integrity sha512-rRgUkpEHWpa5VCT66YscInCQmQuPCB1RFRzkkxMxg4b+jaL0V12E3riWWR2Sh5OIiUhCwGW/ZExuEO4Az32E6Q==
|
||||
|
||||
merge2@^1.3.0:
|
||||
version "1.4.1"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user