feat: 更新 docker-compose 配置,添加 nginx 服务并移除不必要的注释;新增 nginx 配置文件以支持应用部署
This commit is contained in:
@@ -1,29 +1,46 @@
|
|||||||
version: '3.8'
|
version: "3.4"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
riwa-ionic:
|
||||||
container_name: riwa-distribute
|
image: nginx:alpine
|
||||||
build:
|
container_name: riwa-ionic
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- 6999:6999
|
||||||
environment:
|
restart: always
|
||||||
- NODE_ENV=production
|
network_mode: bridge
|
||||||
- HOST=0.0.0.0
|
|
||||||
- PORT=3000
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./.output/:/app/.output/
|
# dist
|
||||||
networks:
|
- ./.output/public:/usr/share/nginx/html/
|
||||||
- riwa-network
|
# nginx conf
|
||||||
healthcheck:
|
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/api/version"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 20s
|
|
||||||
|
|
||||||
networks:
|
|
||||||
riwa-network:
|
# version: '3.8'
|
||||||
driver: bridge
|
|
||||||
|
# services:
|
||||||
|
# app:
|
||||||
|
# container_name: riwa-distribute
|
||||||
|
# build:
|
||||||
|
# context: .
|
||||||
|
# dockerfile: Dockerfile
|
||||||
|
# ports:
|
||||||
|
# - "3000:3000"
|
||||||
|
# environment:
|
||||||
|
# - NODE_ENV=production
|
||||||
|
# - HOST=0.0.0.0
|
||||||
|
# - PORT=3000
|
||||||
|
# restart: unless-stopped
|
||||||
|
# volumes:
|
||||||
|
# - ./.output/:/app/.output/
|
||||||
|
# networks:
|
||||||
|
# - riwa-network
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/api/version"]
|
||||||
|
# interval: 30s
|
||||||
|
# timeout: 10s
|
||||||
|
# retries: 3
|
||||||
|
# start_period: 20s
|
||||||
|
|
||||||
|
# networks:
|
||||||
|
# riwa-network:
|
||||||
|
# driver: bridge
|
||||||
|
|||||||
106
packages/distribute/nginx.conf
Normal file
106
packages/distribute/nginx.conf
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# Generated by nginxconfig.io
|
||||||
|
# See nginxconfig.txt for the configuration share link
|
||||||
|
|
||||||
|
user nginx;
|
||||||
|
pid /var/run/nginx.pid;
|
||||||
|
worker_processes auto;
|
||||||
|
worker_rlimit_nofile 65535;
|
||||||
|
|
||||||
|
# Load modules
|
||||||
|
include /etc/nginx/modules-enabled/*.conf;
|
||||||
|
|
||||||
|
events {
|
||||||
|
multi_accept on;
|
||||||
|
worker_connections 65535;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
charset utf-8;
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
server_tokens off;
|
||||||
|
log_not_found off;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
types_hash_bucket_size 64;
|
||||||
|
client_max_body_size 16M;
|
||||||
|
|
||||||
|
# MIME
|
||||||
|
include mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
access_log off;
|
||||||
|
error_log /dev/null;
|
||||||
|
|
||||||
|
# Load configs
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 6999;
|
||||||
|
server_name hdbpage.top;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
|
||||||
|
# SSL
|
||||||
|
# ssl_certificate /etc/nginx/ssl/hdbpage.top.pem;
|
||||||
|
# ssl_certificate_key /etc/nginx/ssl/hdbpage.top.key;
|
||||||
|
# ssl_session_timeout 10m;
|
||||||
|
# ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
# ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
# security headers
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always;
|
||||||
|
add_header Permissions-Policy "interest-cohort=()" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||||
|
|
||||||
|
# . files
|
||||||
|
location ~ /\.(?!well-known) {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
# logging
|
||||||
|
access_log /var/log/nginx/access.log combined buffer=512k flush=1m;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
|
||||||
|
# index.html fallback
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# favicon.ico
|
||||||
|
location = /favicon.ico {
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# robots.txt
|
||||||
|
location = /robots.txt {
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disable HTML caching
|
||||||
|
location ~* \.(?:html?)$ {
|
||||||
|
add_header Cache-Control "no-cache";
|
||||||
|
}
|
||||||
|
|
||||||
|
# assets, media
|
||||||
|
location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
|
||||||
|
expires 7d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# svg, fonts
|
||||||
|
location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
|
||||||
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
expires 7d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# gzip
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,42 +46,8 @@ function openAppDetail(app: AppInfo) {
|
|||||||
navigateTo(`/apps/${app.id}`)
|
navigateTo(`/apps/${app.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建涟漪效果
|
|
||||||
function createRipple(event: MouseEvent | TouchEvent) {
|
|
||||||
const button = event.currentTarget as HTMLElement
|
|
||||||
const ripple = document.createElement('span')
|
|
||||||
const rect = button.getBoundingClientRect()
|
|
||||||
|
|
||||||
const x = ('touches' in event ? event.touches[0]!.clientX : event.clientX) - rect.left
|
|
||||||
const y = ('touches' in event ? event.touches[0]!.clientY : event.clientY) - rect.top
|
|
||||||
|
|
||||||
ripple.style.cssText = `
|
|
||||||
position: absolute;
|
|
||||||
left: ${x}px;
|
|
||||||
top: ${y}px;
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(255, 255, 255, 0.6);
|
|
||||||
transform: translate(-50%, -50%) scale(0);
|
|
||||||
animation: ripple-expand 0.6s ease-out;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 100;
|
|
||||||
`
|
|
||||||
|
|
||||||
button.style.position = 'relative'
|
|
||||||
button.style.overflow = 'hidden'
|
|
||||||
button.appendChild(ripple)
|
|
||||||
|
|
||||||
setTimeout(() => ripple.remove(), 600)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载处理
|
// 下载处理
|
||||||
async function handleDownload(app: AppInfo, type: 'ios' | 'android' | 'h5', event?: MouseEvent | TouchEvent) {
|
async function handleDownload(app: AppInfo, type: 'ios' | 'android' | 'h5', event?: MouseEvent | TouchEvent) {
|
||||||
if (event) {
|
|
||||||
createRipple(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = app.downloads[type]
|
const url = app.downloads[type]
|
||||||
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
@@ -118,7 +84,7 @@ useHead({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen relative overflow-hidden bg-gradient-to-br from-gray-50 via-blue-50/30 to-purple-50/30 dark:from-gray-950 dark:via-blue-950/20 dark:to-purple-950/20">
|
<div class="min-h-screen relative overflow-hidden bg-linear-to-br from-gray-50 via-blue-50/30 to-purple-50/30 dark:from-gray-950 dark:via-blue-950/20 dark:to-purple-950/20">
|
||||||
<!-- 科技感网格背景 -->
|
<!-- 科技感网格背景 -->
|
||||||
<div class="fixed inset-0 opacity-30 dark:opacity-20 pointer-events-none">
|
<div class="fixed inset-0 opacity-30 dark:opacity-20 pointer-events-none">
|
||||||
<div class="absolute inset-0 animate-pulse-subtle" style="background-image: linear-gradient(rgba(99, 102, 241, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(99, 102, 241, 0.1) 1px, transparent 1px); background-size: 50px 50px;"></div>
|
<div class="absolute inset-0 animate-pulse-subtle" style="background-image: linear-gradient(rgba(99, 102, 241, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(99, 102, 241, 0.1) 1px, transparent 1px); background-size: 50px 50px;"></div>
|
||||||
@@ -139,12 +105,12 @@ useHead({
|
|||||||
<header class="sticky top-0 z-50">
|
<header class="sticky top-0 z-50">
|
||||||
<div class="flex items-center justify-between py-4">
|
<div class="flex items-center justify-between py-4">
|
||||||
<div class="flex items-center gap-3 group">
|
<div class="flex items-center gap-3 group">
|
||||||
<div class="size-10 rounded-xl bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center text-white font-bold text-xl relative overflow-hidden transition-all duration-300 group-hover:scale-110 animate-gradient shadow-lg shadow-blue-500/50">
|
<div class="size-10 rounded-xl bg-linear-to-br from-blue-500 to-blue-600 flex items-center justify-center text-white font-bold text-xl relative overflow-hidden transition-all duration-300 group-hover:scale-110 animate-gradient shadow-lg shadow-blue-500/50">
|
||||||
<div class="absolute inset-0 bg-gradient-to-tr from-white/0 via-white/20 to-white/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-700"></div>
|
<div class="absolute inset-0 bg-linear-to-tr from-white/0 via-white/20 to-white/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-700"></div>
|
||||||
<div class="absolute -inset-1 bg-blue-400/30 rounded-xl blur-md opacity-0 group-hover:opacity-100 animate-pulse-ring"></div>
|
<div class="absolute -inset-1 bg-blue-400/30 rounded-xl blur-md opacity-0 group-hover:opacity-100 animate-pulse-ring"></div>
|
||||||
<span class="relative z-10 animate-pulse-subtle">R</span>
|
<span class="relative z-10 animate-pulse-subtle">R</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="text-xl font-bold bg-gradient-to-r from-gray-900 via-primary-600 to-purple-600 dark:from-white dark:via-primary-400 dark:to-purple-400 bg-clip-text text-transparent animate-gradient">
|
<h1 class="text-xl font-bold bg-linear-to-r from-gray-900 via-primary-600 to-purple-600 dark:from-white dark:via-primary-400 dark:to-purple-400 bg-clip-text text-transparent animate-gradient">
|
||||||
{{ t('appName') }}
|
{{ t('appName') }}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -170,30 +136,28 @@ useHead({
|
|||||||
</UContainer>
|
</UContainer>
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<UContainer class="py-8 relative z-10">
|
<UContainer class="py-8">
|
||||||
<!-- Search and Filter -->
|
<!-- Search and Filter -->
|
||||||
<div class="mb-8 space-y-4 animate-fade-in">
|
<div class="mb-8 space-y-4">
|
||||||
<!-- Search -->
|
<!-- Search -->
|
||||||
<div class="relative max-w-2xl mx-auto group">
|
<div class="max-w-2xl mx-auto">
|
||||||
<div class="absolute -inset-1 bg-linear-to-r from-primary-500 via-purple-500 to-blue-500 rounded-xl opacity-0 group-hover:opacity-30 blur transition-all duration-500"></div>
|
|
||||||
<UInput
|
<UInput
|
||||||
v-model="searchKeyword"
|
v-model="searchKeyword"
|
||||||
icon="i-heroicons-magnifying-glass"
|
icon="i-heroicons-magnifying-glass"
|
||||||
size="xl"
|
size="xl"
|
||||||
:placeholder="locale === 'zh-CN' ? '搜索应用...' : 'Search apps...'"
|
:placeholder="locale === 'zh-CN' ? '搜索应用...' : 'Search apps...'"
|
||||||
class="w-full relative"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Categories -->
|
<!-- Categories -->
|
||||||
<div class="flex items-center gap-2 overflow-x-auto pb-2 scrollbar-hide">
|
<div class="flex items-center gap-2 overflow-x-auto pb-2">
|
||||||
<UButton
|
<UButton
|
||||||
v-for="category in categories"
|
v-for="category in categories"
|
||||||
:key="category.id"
|
:key="category.id"
|
||||||
:label="category.name[locale as 'zh-CN' | 'en-US']"
|
:label="category.name[locale as 'zh-CN' | 'en-US']"
|
||||||
:color="selectedCategory === category.id ? 'primary' : 'neutral'"
|
:color="selectedCategory === category.id ? 'primary' : 'neutral'"
|
||||||
:variant="selectedCategory === category.id ? 'solid' : 'ghost'"
|
:variant="selectedCategory === category.id ? 'solid' : 'ghost'"
|
||||||
class="transition-all duration-300 active:scale-90 touch-manipulation"
|
|
||||||
@click="selectedCategory = category.id"
|
@click="selectedCategory = category.id"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -205,33 +169,26 @@ useHead({
|
|||||||
v-for="(app, index) in filteredApps"
|
v-for="(app, index) in filteredApps"
|
||||||
:key="app.id"
|
:key="app.id"
|
||||||
class="group relative animate-fade-in-up transform-gpu"
|
class="group relative animate-fade-in-up transform-gpu"
|
||||||
:style="`animation-delay: ${index * 0.05}s`"
|
|
||||||
>
|
>
|
||||||
<!-- 发光边框效果 -->
|
|
||||||
<div class="absolute -inset-0.5 bg-gradient-to-r from-primary-500 via-purple-500 to-blue-500 rounded-2xl opacity-0 group-hover:opacity-100 blur transition-all duration-500 group-hover:blur-md animate-gradient"></div>
|
|
||||||
|
|
||||||
<!-- 发光轨迹效果 -->
|
|
||||||
<div class="absolute -inset-1 bg-gradient-to-r from-transparent via-primary-400/50 to-transparent rounded-2xl opacity-0 group-active:opacity-100 blur-sm transition-opacity duration-300 animate-pulse-glow"></div>
|
|
||||||
|
|
||||||
<UCard
|
<UCard
|
||||||
class="cursor-pointer backdrop-blur-sm bg-white/90 dark:bg-gray-900/90 border border-gray-200/50 dark:border-gray-800/50 transition-all duration-500 hover:shadow-2xl hover:shadow-primary-500/20 hover:-translate-y-2 hover:rotate-y-2 active:scale-95 active:shadow-lg active:shadow-primary-500/40 relative overflow-hidden touch-manipulation transform-style-3d"
|
class="cursor-pointer backdrop-blur-sm bg-white/90 dark:bg-gray-900/90 border border-gray-200/50 dark:border-gray-800/50 transition-all duration-500 hover:shadow-2xl hover:shadow-primary-500/20 hover:-translate-y-2 hover:rotate-y-2 active:scale-95 active:shadow-lg active:shadow-primary-500/40 relative overflow-hidden touch-manipulation"
|
||||||
@click="openAppDetail(app)"
|
@click="openAppDetail(app)"
|
||||||
>
|
>
|
||||||
<!-- 内部发光效果 -->
|
<!-- 内部发光效果 -->
|
||||||
<div class="absolute inset-0 bg-gradient-to-br from-primary-500/5 via-transparent to-purple-500/5 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
<div class="absolute inset-0 bg-linear-to-br from-primary-500/5 via-transparent to-purple-500/5 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||||
|
|
||||||
<div class="flex items-start gap-4 relative z-10">
|
<div class="flex items-start gap-4 relative z-10">
|
||||||
<!-- App Icon -->
|
<!-- App Icon -->
|
||||||
<div class="size-16 rounded-2xl bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center text-white font-bold text-2xl shrink-0 shadow-lg shadow-blue-500/50 relative overflow-hidden group-hover:shadow-2xl group-hover:shadow-blue-500/60 transition-all duration-500 group-hover:scale-110 group-hover:rotate-3 group-active:scale-105 group-active:rotate-1 animate-float-subtle p-2">
|
<div class="size-16 rounded-2xl bg-linear-to-br from-blue-500 to-blue-600 flex items-center justify-center text-white font-bold text-2xl shrink-0 shadow-lg shadow-blue-500/50 relative overflow-hidden group-hover:shadow-2xl group-hover:shadow-blue-500/60 transition-all duration-500 group-hover:scale-110 group-hover:rotate-3 group-active:scale-105 group-active:rotate-1 animate-float-subtle p-2">
|
||||||
<!-- 动态发光效果 -->
|
<!-- 动态发光效果 -->
|
||||||
<div class="absolute inset-0 bg-gradient-to-tr from-white/0 via-white/30 to-white/0 translate-x-[-100%] group-hover:translate-x-[100%] group-active:translate-x-[50%] transition-transform duration-700"></div>
|
<div class="absolute inset-0 bg-linear-to-tr from-white/0 via-white/30 to-white/0 translate-x-[-100%] group-hover:translate-x-full group-active:translate-x-[50%] transition-transform duration-700"></div>
|
||||||
<!-- 脉冲光环 -->
|
<!-- 脉冲光环 -->
|
||||||
<div class="absolute -inset-2 bg-blue-400/30 rounded-2xl blur-md opacity-0 group-hover:opacity-100 animate-pulse-ring"></div>
|
<div class="absolute -inset-2 bg-blue-400/30 rounded-2xl blur-md opacity-0 group-hover:opacity-100 animate-pulse-ring"></div>
|
||||||
<img :src="app.icon" :alt="app.name" class="size-full object-contain relative z-10 rounded-lg transition-all duration-300 group-active:scale-95 group-hover:rotate-[-3deg]" />
|
<img :src="app.icon" :alt="app.name" class="size-full object-contain relative z-10 rounded-lg transition-all duration-300 group-active:scale-95 group-hover:rotate-[-3deg]" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- App Info -->
|
<!-- App Info -->
|
||||||
<div class="flex-1 min-w-0 animate-slide-in-subtle">
|
<div class="flex-1 min-w-0">
|
||||||
<h3 class="font-bold text-lg text-gray-900 dark:text-white truncate group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors duration-300">
|
<h3 class="font-bold text-lg text-gray-900 dark:text-white truncate group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors duration-300">
|
||||||
{{ app.name }}
|
{{ app.name }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|||||||
Reference in New Issue
Block a user