first commit
Some checks failed
Deploy Docker Image / build-and-deploy (push) Failing after 1m31s

This commit is contained in:
jeremygan2021
2026-02-27 17:03:49 +08:00
parent 99218d9035
commit d1e78559bc
2 changed files with 127 additions and 38 deletions

View File

@@ -0,0 +1,42 @@
name: Deploy Docker Image
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build-and-deploy:
runs-on: ubuntu
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/wx-pyq:latest
- name: Deploy to Server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_KEY }}
script: |
docker pull ${{ secrets.DOCKER_USERNAME }}/wx-pyq:latest
docker stop wx-pyq || true
docker rm wx-pyq || true
docker run -d --name wx-pyq -p 80:80 ${{ secrets.DOCKER_USERNAME }}/wx-pyq:latest

View File

@@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef } from 'react';
import { Button, Input, Card, Toast, ErrorBlock } from 'antd-mobile';
import { Copy, RefreshCw, Sparkles, Wand2, Quote, UserCircle2, Download } from 'lucide-react';
import { Button, Input, Toast, ErrorBlock } from 'antd-mobile';
import { Copy, RefreshCw, Sparkles, Wand2, Quote, UserCircle2, Download, Image as ImageIcon } from 'lucide-react';
import { useAppStore } from '../store/useAppStore';
import { generateCopyStream } from '../services/api';
import { Header } from '../components/Header';
@@ -91,17 +91,44 @@ export const Home: React.FC = () => {
const copyToClipboard = () => {
if (!result) return;
navigator.clipboard.writeText(result).then(() => {
Toast.show({ content: '已复制到剪贴板', icon: 'success' });
Toast.show({ content: '已复制文案', icon: 'success' });
});
};
const copyPosterImage = async () => {
if (!posterRef.current || !result) return;
try {
const canvas = await html2canvas(posterRef.current, {
useCORS: true,
scale: 2,
backgroundColor: null
});
canvas.toBlob(async (blob) => {
if (!blob) return;
try {
// Attempt to write to clipboard
const item = new ClipboardItem({ 'image/png': blob });
await navigator.clipboard.write([item]);
Toast.show({ content: '海报已复制', icon: 'success' });
} catch (err) {
console.error('Clipboard write failed:', err);
Toast.show({ content: '复制失败,请尝试保存', icon: 'fail' });
}
}, 'image/png');
} catch (err) {
console.error('Capture failed:', err);
Toast.show({ content: '生成图片失败', icon: 'fail' });
}
};
const handleSaveImage = async () => {
if (!posterRef.current || !result) return;
try {
const canvas = await html2canvas(posterRef.current, {
useCORS: true,
scale: 2,
backgroundColor: null // Transparent background if needed, but poster usually has bg
backgroundColor: null
});
const link = document.createElement('a');
link.download = `wx-moments-${Date.now()}.png`;
@@ -179,7 +206,7 @@ export const Home: React.FC = () => {
<div className="mt-4 text-center">
<p className="text-xs text-slate-400 dark:text-slate-500">
DashScope ·
DashScope (qwen3.5-plus) ·
</p>
</div>
</div>
@@ -233,7 +260,11 @@ export const Home: React.FC = () => {
</motion.div>
)}
<div ref={posterRef} className="relative rounded-3xl overflow-hidden shadow-2xl transition-all">
{/* Poster Container */}
<div
ref={posterRef}
className="relative rounded-3xl overflow-hidden shadow-2xl transition-all aspect-[3/4] md:aspect-[4/5] max-w-[500px] mx-auto bg-white dark:bg-slate-900"
>
{/* Background Image Layer */}
{currentPosterUrl && (
<div className="absolute inset-0 z-0">
@@ -243,33 +274,36 @@ export const Home: React.FC = () => {
className="w-full h-full object-cover"
crossOrigin="anonymous"
/>
<div className="absolute inset-0 bg-black/40 backdrop-blur-[2px]" /> {/* Overlay for readability */}
<div className="absolute inset-0 bg-black/40 backdrop-blur-[1px]" />
</div>
)}
<Card
className={`${currentPosterUrl ? 'bg-transparent border-0 text-white' : 'bg-white dark:bg-slate-900 border-0'} rounded-3xl overflow-hidden relative z-10`}
bodyClassName="p-0"
style={{ background: currentPosterUrl ? 'transparent' : undefined }}
>
<div className="p-6 md:p-10 min-h-[300px] flex flex-col">
<div className="flex-1 relative">
<Quote className={`absolute -top-2 -left-2 w-16 h-16 transform -scale-x-100 ${currentPosterUrl ? 'text-white/30' : 'text-indigo-100 dark:text-indigo-900/30'}`} />
<div className="relative z-10 h-full flex flex-col p-8 md:p-10">
<Quote className={`w-12 h-12 transform -scale-x-100 mb-6 ${currentPosterUrl ? 'text-white/60' : 'text-indigo-100 dark:text-indigo-900/30'}`} />
<div className={`relative z-10 text-lg md:text-xl leading-relaxed font-medium whitespace-pre-wrap font-sans ${currentPosterUrl ? 'text-white drop-shadow-md' : 'text-slate-700 dark:text-slate-200'}`}>
{result}
{loading && (
<span className={`inline-block w-2 h-5 ml-1 animate-pulse align-middle ${currentPosterUrl ? 'bg-white' : 'bg-indigo-500'}`} />
)}
</div>
<div ref={resultEndRef} />
</div>
</div>
</Card>
<div className={`flex-1 text-xl md:text-2xl leading-relaxed font-medium whitespace-pre-wrap font-sans overflow-y-auto custom-scrollbar ${currentPosterUrl ? 'text-white drop-shadow-md' : 'text-slate-700 dark:text-slate-200'}`}>
{result}
{loading && (
<span className={`inline-block w-2 h-6 ml-1 animate-pulse align-middle ${currentPosterUrl ? 'bg-white' : 'bg-indigo-500'}`} />
)}
<div ref={resultEndRef} />
</div>
<div className={`mt-8 pt-6 border-t ${currentPosterUrl ? 'border-white/20' : 'border-slate-100 dark:border-slate-800'}`}>
<div className="flex items-center justify-between">
<div className={`text-sm ${currentPosterUrl ? 'text-white/80' : 'text-slate-400'}`}>
Generated by AI
</div>
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${currentPosterUrl ? 'bg-white/20' : 'bg-indigo-50'}`}>
<Sparkles size={14} className={currentPosterUrl ? 'text-white' : 'text-indigo-500'} />
</div>
</div>
</div>
</div>
</div>
{/* Actions Bar (Outside of Poster Ref) */}
<div className="mt-6 flex justify-end gap-3">
<div className="mt-6 flex flex-wrap justify-center md:justify-end gap-3">
<Button
size="middle"
fill="none"
@@ -278,7 +312,7 @@ export const Home: React.FC = () => {
className="bg-white/50 dark:bg-slate-800/50 backdrop-blur text-slate-600 dark:text-slate-300 hover:bg-white/80 dark:hover:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700"
>
<RefreshCw size={18} className={`mr-2 ${loading ? 'animate-spin' : ''}`} />
{loading ? '生成中' : '换一个'}
{loading ? '重写' : '换一个'}
</Button>
<Button
@@ -294,16 +328,29 @@ export const Home: React.FC = () => {
</Button>
{currentPosterUrl && result && !loading && (
<Button
size="middle"
color="success"
fill="solid"
onClick={handleSaveImage}
className="rounded-xl px-6 bg-emerald-500 hover:bg-emerald-600 border-none shadow-md shadow-emerald-500/20 flex items-center text-white active:scale-95 transition-transform"
>
<Download size={18} className="mr-2" />
</Button>
<>
<Button
size="middle"
color="warning"
fill="solid"
onClick={copyPosterImage}
className="rounded-xl px-6 bg-amber-500 hover:bg-amber-600 border-none shadow-md shadow-amber-500/20 flex items-center text-white active:scale-95 transition-transform"
>
<ImageIcon size={18} className="mr-2" />
</Button>
<Button
size="middle"
color="success"
fill="solid"
onClick={handleSaveImage}
className="rounded-xl px-6 bg-emerald-500 hover:bg-emerald-600 border-none shadow-md shadow-emerald-500/20 flex items-center text-white active:scale-95 transition-transform"
>
<Download size={18} className="mr-2" />
</Button>
</>
)}
</div>
</motion.div>