哈哈
This commit is contained in:
10
config.py
10
config.py
@@ -25,7 +25,15 @@ class Config:
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL") or _mysql_uri()
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SITE_URL = os.environ.get("SITE_URL") or "https://vps.ddrwode.cn"
|
||||
SITE_NAME = "云价眼"
|
||||
SITE_NAME = os.environ.get("SITE_NAME") or "云价眼"
|
||||
PREFERRED_URL_SCHEME = "https"
|
||||
SEND_FILE_MAX_AGE_DEFAULT = int(os.environ.get("STATIC_CACHE_SECONDS") or "604800")
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
SESSION_COOKIE_SAMESITE = os.environ.get("SESSION_COOKIE_SAMESITE") or "Lax"
|
||||
SESSION_COOKIE_SECURE = (
|
||||
os.environ.get("SESSION_COOKIE_SECURE")
|
||||
or ("1" if (os.environ.get("FLASK_ENV") or "").lower() == "production" else "0")
|
||||
) == "1"
|
||||
|
||||
|
||||
# 兼容直接 from config import XXX
|
||||
|
||||
2
static/ads.txt
Normal file
2
static/ads.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# Add your ad network records in the following format:
|
||||
# example.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0
|
||||
@@ -33,6 +33,20 @@
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.forum-header-inner-with-center {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.forum-header-inner-with-center .forum-header-left {
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.forum-header-inner-with-center .forum-header-right {
|
||||
grid-column: 3;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.forum-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -64,6 +78,33 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.forum-top-nav {
|
||||
grid-column: 2;
|
||||
justify-self: center;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.forum-top-nav a {
|
||||
color: var(--text-muted);
|
||||
text-decoration: none;
|
||||
font-size: 0.88rem;
|
||||
font-family: var(--font-mono);
|
||||
padding: 0.34rem 0.62rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-card);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.forum-top-nav a:hover,
|
||||
.forum-top-nav a.active {
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-glow);
|
||||
}
|
||||
|
||||
.forum-primary-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -124,6 +165,99 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.visually-hidden {
|
||||
position: absolute !important;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.forum-hero {
|
||||
padding: 0.9rem 0.95rem;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
border-radius: var(--radius-lg);
|
||||
margin-bottom: 0.9rem;
|
||||
background:
|
||||
linear-gradient(145deg, rgba(3, 105, 161, 0.07), rgba(15, 23, 42, 0.04)),
|
||||
var(--bg-card);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.forum-hero-kicker {
|
||||
margin: 0;
|
||||
color: var(--accent);
|
||||
font-size: 0.75rem;
|
||||
font-family: var(--font-mono);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.forum-hero h1 {
|
||||
margin: 0.28rem 0 0.38rem;
|
||||
font-size: clamp(1.18rem, 2vw, 1.52rem);
|
||||
color: var(--text);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.forum-hero p {
|
||||
margin: 0;
|
||||
color: var(--text-muted);
|
||||
max-width: 72ch;
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.62;
|
||||
}
|
||||
|
||||
.forum-hero-meta {
|
||||
margin-top: 0.7rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.55rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.forum-hero-meta span {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
padding: 0.2rem 0.54rem;
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.forum-breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.38rem;
|
||||
margin-bottom: 0.72rem;
|
||||
font-size: 0.76rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.forum-breadcrumb a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.forum-breadcrumb a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
a:focus-visible,
|
||||
button:focus-visible,
|
||||
input:focus-visible,
|
||||
select:focus-visible,
|
||||
textarea:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.forum-topline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -335,29 +469,57 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
padding: 0.58rem 0.92rem;
|
||||
padding: 0.72rem 0.92rem;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--bg-card);
|
||||
background: linear-gradient(180deg, rgba(248, 250, 252, 0.75), rgba(248, 250, 252, 0.3));
|
||||
}
|
||||
|
||||
.topic-footer-controls {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.45rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.page-size-form {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.22rem 0.3rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
background: var(--bg-elevated);
|
||||
font-size: 0.78rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.page-size-form label,
|
||||
.page-size-form span {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.76rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-size-form select {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
background: var(--bg-elevated);
|
||||
border-radius: 8px;
|
||||
background: var(--bg-card);
|
||||
color: var(--text);
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.2;
|
||||
padding: 0.26rem 0.42rem;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
min-height: 32px;
|
||||
padding: 0.22rem 0.48rem;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.page-size-form select:hover {
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-glow);
|
||||
}
|
||||
|
||||
.page-size-form select:focus {
|
||||
@@ -498,25 +660,35 @@
|
||||
|
||||
.forum-pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
padding: 0.78rem 0.92rem;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.forum-pagination-inline {
|
||||
padding: 0;
|
||||
border-top: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.page-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 34px;
|
||||
height: 30px;
|
||||
padding: 0 0.55rem;
|
||||
min-width: 36px;
|
||||
height: 32px;
|
||||
padding: 0 0.6rem;
|
||||
text-decoration: none;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-muted);
|
||||
background: var(--bg-card);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
@@ -524,17 +696,22 @@
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-glow);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 14px rgba(3, 105, 161, 0.14);
|
||||
}
|
||||
|
||||
.page-link.active {
|
||||
color: #fff;
|
||||
border-color: var(--accent);
|
||||
background: var(--accent);
|
||||
box-shadow: 0 8px 16px rgba(3, 105, 161, 0.2);
|
||||
}
|
||||
|
||||
.page-link.disabled {
|
||||
color: var(--text-muted);
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.forum-sidebar {
|
||||
@@ -1126,6 +1303,24 @@
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.forum-hero {
|
||||
padding: 0.78rem 0.8rem;
|
||||
}
|
||||
|
||||
.forum-hero-meta {
|
||||
gap: 0.42rem;
|
||||
}
|
||||
|
||||
.forum-header-inner-with-center {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.forum-top-nav {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.forum-header {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
@@ -1182,6 +1377,21 @@
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.topic-footer-controls {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.forum-pagination-inline {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.page-size-form {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.topic-head {
|
||||
grid-template-columns: minmax(0, 1fr) 64px 64px;
|
||||
}
|
||||
@@ -1241,6 +1451,7 @@
|
||||
}
|
||||
|
||||
.forum-primary-nav a,
|
||||
.forum-top-nav a,
|
||||
.forum-link {
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -1320,9 +1531,36 @@
|
||||
background: rgba(3, 105, 161, 0.08);
|
||||
}
|
||||
|
||||
.topic-footer {
|
||||
border-top-color: rgba(148, 163, 184, 0.3);
|
||||
background: linear-gradient(180deg, rgba(248, 250, 252, 0.78), rgba(248, 250, 252, 0.42));
|
||||
}
|
||||
|
||||
.page-size-form {
|
||||
border-color: rgba(148, 163, 184, 0.38);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.page-size-form label,
|
||||
.page-size-form span {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.page-size-form select {
|
||||
border-color: rgba(148, 163, 184, 0.38);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.page-size-form select:hover {
|
||||
border-color: rgba(3, 105, 161, 0.45);
|
||||
background: rgba(3, 105, 161, 0.08);
|
||||
}
|
||||
|
||||
.page-link {
|
||||
border-color: rgba(148, 163, 184, 0.38);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.page-link.active {
|
||||
|
||||
@@ -2965,3 +2965,233 @@ html {
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
SEO + Compliance Sections
|
||||
========================================================================== */
|
||||
|
||||
a:focus-visible,
|
||||
button:focus-visible,
|
||||
input:focus-visible,
|
||||
select:focus-visible,
|
||||
summary:focus-visible {
|
||||
outline: 2px solid #0369a1;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.no-js-note {
|
||||
margin: 0;
|
||||
padding: 0.72rem 0.86rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.3);
|
||||
background: rgba(241, 245, 249, 0.9);
|
||||
color: #334155;
|
||||
font-size: 0.83rem;
|
||||
}
|
||||
|
||||
.seo-faq {
|
||||
border: 1px solid rgba(148, 163, 184, 0.3);
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08), 0 16px 26px rgba(15, 23, 42, 0.06);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.seo-faq .section-head h2 {
|
||||
margin: 0;
|
||||
color: #020617;
|
||||
font-size: 1.02rem;
|
||||
}
|
||||
|
||||
.seo-faq .section-head p {
|
||||
margin: 0.34rem 0 0;
|
||||
color: #64748b;
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
.faq-grid {
|
||||
margin-top: 0.78rem;
|
||||
display: grid;
|
||||
gap: 0.62rem;
|
||||
}
|
||||
|
||||
.faq-item {
|
||||
border: 1px solid rgba(148, 163, 184, 0.3);
|
||||
border-radius: 12px;
|
||||
background: #ffffff;
|
||||
padding: 0.66rem 0.78rem;
|
||||
}
|
||||
|
||||
.faq-item summary {
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
color: #0f172a;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.faq-item summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.faq-item p {
|
||||
margin: 0.5rem 0 0;
|
||||
color: #334155;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.commercial-cta {
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(15, 23, 42, 0.14);
|
||||
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
||||
color: #f8fafc;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.commercial-cta h2 {
|
||||
margin: 0;
|
||||
font-size: 1.08rem;
|
||||
}
|
||||
|
||||
.commercial-cta p {
|
||||
margin: 0.4rem 0 0;
|
||||
color: rgba(226, 232, 240, 0.9);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.cta-actions {
|
||||
margin-top: 0.78rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.58rem;
|
||||
}
|
||||
|
||||
.cta-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 38px;
|
||||
border-radius: 10px;
|
||||
padding: 0.48rem 0.84rem;
|
||||
font-size: 0.86rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.cta-btn-primary {
|
||||
background: #0ea5e9;
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
.cta-btn-primary:hover {
|
||||
background: #0284c7;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.cta-btn-secondary {
|
||||
border: 1px solid rgba(226, 232, 240, 0.45);
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
.cta-btn-secondary:hover {
|
||||
border-color: rgba(226, 232, 240, 0.8);
|
||||
background: rgba(15, 23, 42, 0.28);
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
margin: 0.5rem 0;
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.7rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: #334155;
|
||||
text-decoration: none;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: #0f172a;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.policy-page {
|
||||
background:
|
||||
radial-gradient(1100px 460px at 10% -10%, rgba(3, 105, 161, 0.07), transparent 60%),
|
||||
radial-gradient(900px 430px at 90% -15%, rgba(15, 23, 42, 0.08), transparent 62%),
|
||||
var(--bg);
|
||||
}
|
||||
|
||||
.policy-shell {
|
||||
max-width: 920px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding-top: 1.25rem;
|
||||
padding-bottom: 1.7rem;
|
||||
}
|
||||
|
||||
.policy-card {
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.3);
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08), 0 14px 26px rgba(15, 23, 42, 0.07);
|
||||
padding: 1.1rem;
|
||||
}
|
||||
|
||||
.policy-card h1 {
|
||||
margin: 0;
|
||||
font-size: clamp(1.3rem, 2.5vw, 1.82rem);
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.policy-updated {
|
||||
margin: 0.45rem 0 1rem;
|
||||
color: #64748b;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.policy-card section + section {
|
||||
margin-top: 0.82rem;
|
||||
}
|
||||
|
||||
.policy-card h2 {
|
||||
margin: 0;
|
||||
font-size: 0.95rem;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.policy-card p {
|
||||
margin: 0.38rem 0 0;
|
||||
color: #334155;
|
||||
font-size: 0.91rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.policy-card a {
|
||||
color: #0369a1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.policy-card a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.seo-faq,
|
||||
.commercial-cta,
|
||||
.policy-card {
|
||||
border-radius: 14px;
|
||||
padding: 0.9rem;
|
||||
}
|
||||
|
||||
.cta-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cta-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@
|
||||
'<td>' + plan.traffic + '</td>' +
|
||||
'<td class="col-price">' + displayPrice + '</td>' +
|
||||
'<td class="col-link">' +
|
||||
'<a href="' + escapeHtml(plan.official_url) + '" target="_blank" rel="noopener" class="btn-link">' + btnText + '</a>' +
|
||||
'<a href="' + escapeHtml(plan.official_url) + '" target="_blank" rel="noopener noreferrer nofollow" class="btn-link">' + btnText + '</a>' +
|
||||
'</td>';
|
||||
|
||||
return tr;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex,follow">
|
||||
<title>{{ l('用户登录', 'Login') }} - {{ l('云价眼', 'VPS Price') }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex,follow">
|
||||
<title>{{ l('用户注册', 'Register') }} - {{ l('云价眼', 'VPS Price') }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex,follow">
|
||||
<title>{{ l('编辑评论', 'Edit Comment') }} - {{ l('论坛', 'Forum') }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
|
||||
@@ -3,8 +3,35 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ l('论坛', 'Forum') }} - {{ l('云价眼', 'VPS Price') }}</title>
|
||||
<title>{{ seo.title if seo else (l('论坛', 'Forum') ~ ' - ' ~ l('云价眼', 'VPS Price')) }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
{% if seo %}
|
||||
<meta name="description" content="{{ seo.description }}">
|
||||
<meta name="keywords" content="{{ seo.keywords }}">
|
||||
<meta name="robots" content="{{ seo.robots }}">
|
||||
<meta name="theme-color" content="#0F172A">
|
||||
<link rel="canonical" href="{{ seo.canonical_url }}">
|
||||
{% if seo.prev_canonical_url %}<link rel="prev" href="{{ seo.prev_canonical_url }}">{% endif %}
|
||||
{% if seo.next_canonical_url %}<link rel="next" href="{{ seo.next_canonical_url }}">{% endif %}
|
||||
{% for hreflang, href in seo.alternate_links.items() %}
|
||||
<link rel="alternate" hreflang="{{ hreflang }}" href="{{ href }}">
|
||||
{% endfor %}
|
||||
{% if seo.feed_url %}
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ l('论坛 RSS 订阅', 'Forum RSS Feed') }}" href="{{ seo.feed_url }}">
|
||||
{% endif %}
|
||||
<meta property="og:type" content="{{ seo.og_type }}">
|
||||
<meta property="og:url" content="{{ seo.og_url }}">
|
||||
<meta property="og:title" content="{{ seo.og_title }}">
|
||||
<meta property="og:description" content="{{ seo.og_description }}">
|
||||
<meta property="og:image" content="{{ seo.og_image }}">
|
||||
<meta name="twitter:card" content="{{ seo.twitter_card }}">
|
||||
<meta name="twitter:title" content="{{ seo.twitter_title }}">
|
||||
<meta name="twitter:description" content="{{ seo.twitter_description }}">
|
||||
<meta name="twitter:image" content="{{ seo.og_image }}">
|
||||
{% if seo_schema %}
|
||||
<script type="application/ld+json">{{ seo_schema | tojson }}</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<link rel="stylesheet" href="/static/css/forum.css">
|
||||
</head>
|
||||
@@ -12,20 +39,17 @@
|
||||
{% set cards = post_cards if post_cards is defined else [] %}
|
||||
{% set sb = sidebar if sidebar is defined else {'total_users': 0, 'total_posts': 0, 'total_comments': 0, 'category_counts': [], 'active_users': []} %}
|
||||
<header class="forum-header">
|
||||
<div class="forum-header-inner">
|
||||
<div class="forum-header-inner forum-header-inner-with-center">
|
||||
<div class="forum-header-left">
|
||||
<a href="{{ url_for('forum_index') }}" class="forum-logo">
|
||||
<img src="{{ url_for('static', filename='img/site-logo-mark.svg') }}" alt="{{ l('云价眼 Logo', 'VPS Price Logo') }}">
|
||||
<span>{{ l('云价眼论坛', 'VPS Price Forum') }}</span>
|
||||
</a>
|
||||
<nav class="forum-primary-nav">
|
||||
{% for item in tab_links %}
|
||||
<a href="{{ item.url }}" class="{{ 'active' if item.active else '' }}">{{ item.label }}</a>
|
||||
{% endfor %}
|
||||
<a href="{{ category_nav_url }}" class="{{ 'active' if selected_category else '' }}">{{ l('分类', 'Categories') }}</a>
|
||||
</div>
|
||||
<nav class="forum-top-nav" aria-label="{{ l('站点导航', 'Site navigation') }}">
|
||||
<a href="{{ url_for('forum_index') }}" class="active">{{ l('论坛', 'Forum') }}</a>
|
||||
<a href="{{ url_for('index') }}">{{ l('价格表', 'Pricing') }}</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="forum-header-right">
|
||||
<span class="lang-switch">
|
||||
<a href="{{ lang_url('zh') }}" class="{{ 'active' if lang == 'zh' else '' }}" title="切换到中文">中文</a>
|
||||
@@ -46,6 +70,16 @@
|
||||
</header>
|
||||
|
||||
<main class="forum-shell">
|
||||
<section class="forum-hero" aria-labelledby="forum-hero-title">
|
||||
<p class="forum-hero-kicker">{{ l('商业化社区入口', 'Commercial Community Hub') }}</p>
|
||||
<h1 id="forum-hero-title">{{ forum_heading if forum_heading is defined else l('VPS 社区论坛', 'VPS Community Forum') }}</h1>
|
||||
<p>{{ forum_intro if forum_intro is defined else l('聚合 VPS 评测、运维经验与采购讨论。', 'A discussion space for VPS reviews, operations, and buying decisions.') }}</p>
|
||||
<div class="forum-hero-meta">
|
||||
<span>{{ l('帖子总数', 'Total topics') }} {{ sb.total_posts }}</span>
|
||||
<span>{{ l('活跃用户', 'Active users') }} {{ sb.total_users }}</span>
|
||||
<a href="{{ forum_feed_url if forum_feed_url is defined else seo.feed_url }}" class="forum-link">{{ l('RSS 订阅', 'RSS Feed') }}</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="forum-topline">
|
||||
<div class="forum-tabs">
|
||||
{% for item in tab_links %}
|
||||
@@ -77,7 +111,8 @@
|
||||
{% if selected_category %}
|
||||
<input type="hidden" name="category" value="{{ selected_category }}">
|
||||
{% endif %}
|
||||
<input type="text" name="q" value="{{ search_query or '' }}" placeholder="{{ l('搜索标题、正文、作者', 'Search title, content, author') }}" maxlength="80">
|
||||
<label for="forum-search-q" class="visually-hidden">{{ l('搜索帖子', 'Search topics') }}</label>
|
||||
<input id="forum-search-q" type="text" name="q" value="{{ search_query or '' }}" placeholder="{{ l('搜索标题、正文、作者', 'Search title, content, author') }}" maxlength="80">
|
||||
<button type="submit" class="forum-btn-primary">{{ l('搜索', 'Search') }}</button>
|
||||
{% if search_query %}
|
||||
<a href="{{ clear_search_url }}" class="forum-btn-muted">{{ l('清空搜索', 'Clear Search') }}</a>
|
||||
@@ -86,7 +121,7 @@
|
||||
<a href="{{ clear_all_url }}" class="forum-btn-muted">{{ l('重置全部', 'Reset All') }}</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
<div class="category-bar">
|
||||
<nav class="category-bar" aria-label="{{ l('论坛分类', 'Forum categories') }}">
|
||||
{% for item in category_links %}
|
||||
<a href="{{ item.url }}" class="category-chip {{ 'active' if item.active else '' }}">
|
||||
<span>{{ item.name }}</span>
|
||||
@@ -95,11 +130,12 @@
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
<section class="forum-layout">
|
||||
<section class="forum-layout" aria-labelledby="forum-topic-list-title">
|
||||
<div class="topic-stream">
|
||||
<h2 id="forum-topic-list-title" class="visually-hidden">{{ l('论坛主题列表', 'Forum topic list') }}</h2>
|
||||
<div class="topic-head">
|
||||
<div class="topic-col-main">{{ l('主题', 'Topic') }}</div>
|
||||
<div class="topic-col-mini">{{ l('回复', 'Replies') }}</div>
|
||||
@@ -115,7 +151,7 @@
|
||||
<div class="topic-main">
|
||||
<div class="topic-avatar">{{ item.author_initial }}</div>
|
||||
<div class="topic-content">
|
||||
<a href="{{ url_for('forum_post_detail', post_id=post.id) }}" class="topic-title">
|
||||
<a href="{{ url_for('forum_post_detail', post_id=post.id) }}" rel="bookmark" class="topic-title">
|
||||
{% if post.is_pinned %}<span class="topic-flag flag-pinned">{{ l('置顶', 'Pinned') }}</span>{% endif %}
|
||||
{% if post.is_featured %}<span class="topic-flag flag-featured">{{ l('精华', 'Featured') }}</span>{% endif %}
|
||||
{% if post.is_locked %}<span class="topic-flag flag-locked">{{ l('锁帖', 'Locked') }}</span>{% endif %}
|
||||
@@ -124,7 +160,9 @@
|
||||
<div class="topic-meta">
|
||||
<span class="topic-category">{{ post.category or l('综合讨论', 'General') }}</span>
|
||||
<span>{{ item.author_name }}</span>
|
||||
<span>{{ post.created_at.strftime('%Y-%m-%d %H:%M') if post.created_at else '' }}</span>
|
||||
{% if post.created_at %}
|
||||
<time datetime="{{ post.created_at.strftime('%Y-%m-%dT%H:%M:%SZ') }}">{{ post.created_at.strftime('%Y-%m-%d %H:%M') }}</time>
|
||||
{% endif %}
|
||||
<span>{{ l('点赞', 'Likes') }} {{ item.like_count }}</span>
|
||||
<span>{{ l('收藏', 'Bookmarks') }} {{ item.bookmark_count }}</span>
|
||||
</div>
|
||||
@@ -132,12 +170,25 @@
|
||||
</div>
|
||||
<div class="topic-stat">{{ item.reply_count }}</div>
|
||||
<div class="topic-stat">{{ item.view_count }}</div>
|
||||
<div class="topic-stat topic-activity">{{ item.latest_activity_text }}</div>
|
||||
<div class="topic-stat topic-activity" title="{{ item.latest_activity.strftime('%Y-%m-%d %H:%M') if item.latest_activity else '' }}">{{ item.latest_activity_text }}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div class="topic-empty">{{ empty_hint }}</div>
|
||||
{% endif %}
|
||||
{% if total_posts %}
|
||||
<div class="topic-footer">
|
||||
<div class="topic-result">
|
||||
{% if lang == 'en' %}
|
||||
Page {{ current_page }} / {{ total_pages }} · Showing {{ result_start }} - {{ result_end }} of {{ total_posts }}
|
||||
{% else %}
|
||||
第 {{ current_page }} / {{ total_pages }} 页 · 显示 {{ result_start }} - {{ result_end }} / 共 {{ total_posts }} 条
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="topic-footer-controls">
|
||||
{% if total_pages > 1 %}
|
||||
<nav class="forum-pagination" aria-label="{{ l('帖子分页', 'Post pagination') }}">
|
||||
<nav class="forum-pagination forum-pagination-inline" aria-label="{{ l('帖子分页', 'Post pagination') }}">
|
||||
{% if has_prev %}
|
||||
<a href="{{ prev_page_url }}" class="page-link">{{ l('上一页', 'Prev') }}</a>
|
||||
{% else %}
|
||||
@@ -153,18 +204,6 @@
|
||||
{% endif %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="topic-empty">{{ empty_hint }}</div>
|
||||
{% endif %}
|
||||
{% if total_posts %}
|
||||
<div class="topic-footer">
|
||||
<div class="topic-result">
|
||||
{% if lang == 'en' %}
|
||||
Page {{ current_page }} / {{ total_pages }} · Showing {{ result_start }} - {{ result_end }} of {{ total_posts }}
|
||||
{% else %}
|
||||
第 {{ current_page }} / {{ total_pages }} 页 · 显示 {{ result_start }} - {{ result_end }} / 共 {{ total_posts }} 条
|
||||
{% endif %}
|
||||
</div>
|
||||
<form method="get" action="{{ url_for('forum_index') }}" class="page-size-form">
|
||||
<input type="hidden" name="tab" value="{{ active_tab }}">
|
||||
{% if selected_category %}
|
||||
@@ -183,6 +222,7 @@
|
||||
<noscript><button type="submit" class="forum-btn-muted">{{ l('应用', 'Apply') }}</button></noscript>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex,follow">
|
||||
<title>{{ l('通知中心', 'Notifications') }} - {{ l('论坛', 'Forum') }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
|
||||
@@ -3,8 +3,36 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ post.title }} - {{ l('论坛', 'Forum') }}</title>
|
||||
<title>{{ seo.title if seo else (post.title ~ ' - ' ~ l('论坛', 'Forum')) }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
{% if seo %}
|
||||
<meta name="description" content="{{ seo.description }}">
|
||||
<meta name="keywords" content="{{ seo.keywords }}">
|
||||
<meta name="robots" content="{{ seo.robots }}">
|
||||
<meta name="theme-color" content="#0F172A">
|
||||
<link rel="canonical" href="{{ seo.canonical_url }}">
|
||||
{% for hreflang, href in seo.alternate_links.items() %}
|
||||
<link rel="alternate" hreflang="{{ hreflang }}" href="{{ href }}">
|
||||
{% endfor %}
|
||||
{% if seo.feed_url %}
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ l('论坛 RSS 订阅', 'Forum RSS Feed') }}" href="{{ seo.feed_url }}">
|
||||
{% endif %}
|
||||
<meta property="og:type" content="{{ seo.og_type }}">
|
||||
<meta property="og:url" content="{{ seo.og_url }}">
|
||||
<meta property="og:title" content="{{ seo.og_title }}">
|
||||
<meta property="og:description" content="{{ seo.og_description }}">
|
||||
<meta property="og:image" content="{{ seo.og_image }}">
|
||||
<meta property="article:published_time" content="{{ seo.article_published_time }}">
|
||||
<meta property="article:modified_time" content="{{ seo.article_modified_time }}">
|
||||
<meta property="article:section" content="{{ seo.article_section }}">
|
||||
<meta name="twitter:card" content="{{ seo.twitter_card }}">
|
||||
<meta name="twitter:title" content="{{ seo.twitter_title }}">
|
||||
<meta name="twitter:description" content="{{ seo.twitter_description }}">
|
||||
<meta name="twitter:image" content="{{ seo.og_image }}">
|
||||
{% if seo_schema %}
|
||||
<script type="application/ld+json">{{ seo_schema | tojson }}</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<link rel="stylesheet" href="/static/css/forum.css">
|
||||
</head>
|
||||
@@ -45,6 +73,17 @@
|
||||
</header>
|
||||
|
||||
<main class="forum-shell">
|
||||
<nav class="forum-breadcrumb" aria-label="{{ l('面包屑导航', 'Breadcrumb') }}">
|
||||
<a href="{{ url_for('index') }}">{{ l('首页', 'Home') }}</a>
|
||||
<span>/</span>
|
||||
<a href="{{ url_for('forum_index') }}">{{ l('论坛', 'Forum') }}</a>
|
||||
{% if post.category %}
|
||||
<span>/</span>
|
||||
<a href="{{ url_for('forum_index', category=post.category) }}">{{ post.category }}</a>
|
||||
{% endif %}
|
||||
<span>/</span>
|
||||
<span>{{ post.title }}</span>
|
||||
</nav>
|
||||
<section class="forum-layout">
|
||||
<div class="topic-stream">
|
||||
<article class="topic-post-card">
|
||||
@@ -53,7 +92,9 @@
|
||||
{% if post.is_pinned %}<span class="topic-flag flag-pinned">{{ l('置顶', 'Pinned') }}</span>{% endif %}
|
||||
{% if post.is_featured %}<span class="topic-flag flag-featured">{{ l('精华', 'Featured') }}</span>{% endif %}
|
||||
{% if post.is_locked %}<span class="topic-flag flag-locked">{{ l('锁帖', 'Locked') }}</span>{% endif %}
|
||||
<span>{{ post.created_at.strftime('%Y-%m-%d %H:%M') if post.created_at else '' }}</span>
|
||||
{% if post.created_at %}
|
||||
<time datetime="{{ post.created_at.strftime('%Y-%m-%dT%H:%M:%SZ') }}">{{ post.created_at.strftime('%Y-%m-%d %H:%M') }}</time>
|
||||
{% endif %}
|
||||
<span>{{ l('浏览', 'Views') }} {{ post.view_count or 0 }}</span>
|
||||
<span>{{ l('点赞', 'Likes') }} {{ like_count or 0 }}</span>
|
||||
<span>{{ l('收藏', 'Bookmarks') }} {{ bookmark_count or 0 }}</span>
|
||||
@@ -125,12 +166,14 @@
|
||||
{% if comments %}
|
||||
<ul class="comment-stream">
|
||||
{% for c in comments %}
|
||||
<li class="comment-row">
|
||||
<li id="comment-{{ c.id }}" class="comment-row">
|
||||
<div class="comment-avatar">{{ (c.author_rel.username[0] if c.author_rel and c.author_rel.username else '?')|upper }}</div>
|
||||
<div class="comment-body">
|
||||
<div class="comment-head">
|
||||
<span class="comment-author">{{ c.author_rel.username if c.author_rel else l('已注销用户', 'Deleted user') }}</span>
|
||||
<span>{{ c.created_at.strftime('%Y-%m-%d %H:%M') if c.created_at else '' }}</span>
|
||||
{% if c.created_at %}
|
||||
<time datetime="{{ c.created_at.strftime('%Y-%m-%dT%H:%M:%SZ') }}">{{ c.created_at.strftime('%Y-%m-%d %H:%M') }}</time>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="comment-content md-content">{{ c.content|markdown_html }}</div>
|
||||
<div class="comment-actions">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex,follow">
|
||||
<title>{{ l('发布帖子', 'Topic Editor') }} - {{ l('云价眼', 'VPS Price') }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex,follow">
|
||||
<title>{{ l('个人中心', 'Profile') }} - {{ l('论坛', 'Forum') }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
|
||||
@@ -3,37 +3,28 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ t.meta_title }}</title>
|
||||
<title>{{ seo.title }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
<meta name="description" content="{{ t.meta_description }}">
|
||||
<meta name="keywords" content="{{ t.meta_keywords }}">
|
||||
<link rel="canonical" href="{{ site_url }}/">
|
||||
<!-- Open Graph / 社交分享 -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="{{ site_url }}/">
|
||||
<meta property="og:title" content="{{ t.og_title }}">
|
||||
<meta property="og:description" content="{{ t.og_description }}">
|
||||
<meta name="description" content="{{ seo.description }}">
|
||||
<meta name="keywords" content="{{ seo.keywords }}">
|
||||
<meta name="robots" content="{{ seo.robots }}">
|
||||
<meta name="theme-color" content="#0F172A">
|
||||
<link rel="canonical" href="{{ seo.canonical_url }}">
|
||||
{% for hreflang, href in seo.alternate_links.items() %}
|
||||
<link rel="alternate" hreflang="{{ hreflang }}" href="{{ href }}">
|
||||
{% endfor %}
|
||||
<meta property="og:type" content="{{ seo.og_type }}">
|
||||
<meta property="og:url" content="{{ seo.og_url }}">
|
||||
<meta property="og:title" content="{{ seo.og_title }}">
|
||||
<meta property="og:description" content="{{ seo.og_description }}">
|
||||
<meta property="og:image" content="{{ seo.og_image }}">
|
||||
<meta property="og:site_name" content="{{ site_name }}">
|
||||
<meta property="og:locale" content="{{ t.og_locale }}">
|
||||
<!-- JSON-LD 结构化数据,便于搜索引擎理解 -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebApplication",
|
||||
"name": "{{ site_name }}",
|
||||
"description": "{{ t.schema_webapp_description }}",
|
||||
"url": "{{ site_url }}",
|
||||
"applicationCategory": "UtilitiesApplication"
|
||||
}
|
||||
</script>
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Table",
|
||||
"about": "{{ t.schema_table_about }}",
|
||||
"name": "{{ t.schema_table_name }}",
|
||||
"description": "{{ t.schema_table_description }}"
|
||||
}
|
||||
</script>
|
||||
<meta name="twitter:card" content="{{ seo.twitter_card }}">
|
||||
<meta name="twitter:title" content="{{ seo.twitter_title }}">
|
||||
<meta name="twitter:description" content="{{ seo.twitter_description }}">
|
||||
<meta name="twitter:image" content="{{ seo.og_image }}">
|
||||
<script type="application/ld+json">{{ seo_schema | tojson }}</script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Noto+Sans+SC:wght@400;500;700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
@@ -44,7 +35,7 @@
|
||||
<div class="header-inner">
|
||||
<div class="header-brand">
|
||||
<a href="{{ url_for('index', lang=lang) }}" class="site-logo-link" aria-label="{{ site_name }}">
|
||||
<img src="{{ url_for('static', filename='img/site-logo.svg') }}" alt="{{ site_name }} Logo" class="site-logo">
|
||||
<img src="{{ url_for('static', filename='img/site-logo.svg') }}" alt="{{ site_name }} Logo" class="site-logo" loading="eager" fetchpriority="high">
|
||||
</a>
|
||||
<p class="tagline">{{ t.tagline }}</p>
|
||||
</div>
|
||||
@@ -193,12 +184,72 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="table-body">
|
||||
<!-- 由 JS 填充 -->
|
||||
{% if initial_plans_json %}
|
||||
{% for plan in initial_plans_json %}
|
||||
<tr>
|
||||
<td>{{ plan.provider or '-' }}</td>
|
||||
<td>{{ plan.countries or '-' }}</td>
|
||||
<td>{{ plan.name or '-' }}</td>
|
||||
<td>{{ plan.vcpu if plan.vcpu is not none else '-' }}</td>
|
||||
<td>{{ (plan.memory_gb ~ ' GB') if plan.memory_gb is not none else '-' }}</td>
|
||||
<td>{{ (plan.storage_gb ~ ' GB') if plan.storage_gb is not none else '-' }}</td>
|
||||
<td>{{ (plan.bandwidth_mbps ~ ' Mbps') if plan.bandwidth_mbps is not none else '-' }}</td>
|
||||
<td>{{ plan.traffic or '-' }}</td>
|
||||
<td class="col-price">
|
||||
{% if plan.price_cny is not none %}
|
||||
¥{{ '%.2f'|format(plan.price_cny) }}
|
||||
{% elif plan.price_usd is not none %}
|
||||
${{ '%.2f'|format(plan.price_usd) }}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="col-link">
|
||||
{% if plan.official_url %}
|
||||
<a href="{{ plan.official_url }}" target="_blank" rel="noopener noreferrer nofollow" class="btn-link">{{ t.btn_visit }}</a>
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="10" style="text-align:center; padding: 2rem; color: var(--text-muted);">{{ t.empty_state }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<noscript>
|
||||
<p class="no-js-note">{{ t.no_js_note }}</p>
|
||||
</noscript>
|
||||
<p class="disclaimer">{{ t.disclaimer }}</p>
|
||||
|
||||
<section class="seo-faq" aria-labelledby="faq-title">
|
||||
<div class="section-head">
|
||||
<h2 id="faq-title">{{ t.faq_title }}</h2>
|
||||
<p>{{ t.faq_intro }}</p>
|
||||
</div>
|
||||
<div class="faq-grid">
|
||||
{% for item in faq_items %}
|
||||
<details class="faq-item" {% if loop.first %}open{% endif %}>
|
||||
<summary>{{ item.question }}</summary>
|
||||
<p>{{ item.answer }}</p>
|
||||
</details>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="commercial-cta" aria-label="{{ t.cta_title }}">
|
||||
<h2>{{ t.cta_title }}</h2>
|
||||
<p>{{ t.cta_lede }}</p>
|
||||
<div class="cta-actions">
|
||||
<a href="{{ url_for('forum_index') }}" class="cta-btn cta-btn-primary">{{ t.cta_primary }}</a>
|
||||
<a href="https://t.me/dockerse" target="_blank" rel="noopener noreferrer" class="cta-btn cta-btn-secondary">{{ t.cta_secondary }}</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
@@ -207,6 +258,11 @@
|
||||
<!-- 在此处粘贴 Google AdSense 代码 -->
|
||||
</div>
|
||||
<p>{{ t.footer_note }}</p>
|
||||
<nav class="footer-links" aria-label="{{ l('站点链接', 'Site links') }}">
|
||||
<a href="{{ url_for('privacy_policy') }}">{{ l('隐私政策', 'Privacy Policy') }}</a>
|
||||
<a href="{{ url_for('terms_of_service') }}">{{ l('服务条款', 'Terms of Service') }}</a>
|
||||
<a href="{{ url_for('forum_index') }}">{{ l('社区论坛', 'Community Forum') }}</a>
|
||||
</nav>
|
||||
<div class="contact-section">
|
||||
<p class="contact-label">{{ t.contact_label }}</p>
|
||||
<a href="https://t.me/dockerse" target="_blank" rel="noopener" class="contact-link">
|
||||
@@ -236,6 +292,6 @@
|
||||
// 首屏直出数据,避免等待 /api/plans 再渲染表格,加快首屏
|
||||
window.__INITIAL_PLANS__ = {{ initial_plans_json|tojson }};
|
||||
</script>
|
||||
<script src="/static/js/main-simple.js"></script>
|
||||
<script src="/static/js/main-simple.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
87
templates/privacy.html
Normal file
87
templates/privacy.html
Normal file
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ 'zh-CN' if lang == 'zh' else 'en' }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ seo.title }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
<meta name="description" content="{{ seo.description }}">
|
||||
<meta name="robots" content="{{ seo.robots }}">
|
||||
<meta name="theme-color" content="#0F172A">
|
||||
<link rel="canonical" href="{{ seo.canonical_url }}">
|
||||
{% for hreflang, href in seo.alternate_links.items() %}
|
||||
<link rel="alternate" hreflang="{{ hreflang }}" href="{{ href }}">
|
||||
{% endfor %}
|
||||
<meta property="og:type" content="{{ seo.og_type }}">
|
||||
<meta property="og:url" content="{{ seo.og_url }}">
|
||||
<meta property="og:title" content="{{ seo.og_title }}">
|
||||
<meta property="og:description" content="{{ seo.og_description }}">
|
||||
<meta property="og:image" content="{{ seo.og_image }}">
|
||||
<meta name="twitter:card" content="{{ seo.twitter_card }}">
|
||||
<meta name="twitter:title" content="{{ seo.twitter_title }}">
|
||||
<meta name="twitter:description" content="{{ seo.twitter_description }}">
|
||||
<script type="application/ld+json">{{ seo_schema | tojson }}</script>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body class="policy-page">
|
||||
<header class="header">
|
||||
<div class="header-inner">
|
||||
<div class="header-brand">
|
||||
<a href="{{ url_for('index') }}" class="site-logo-link" aria-label="{{ l('云价眼首页', 'VPS Price home') }}">
|
||||
<img src="{{ url_for('static', filename='img/site-logo.svg') }}" alt="{{ l('云价眼 Logo', 'VPS Price Logo') }}" class="site-logo" loading="eager" fetchpriority="high">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="header-nav">
|
||||
<span class="lang-switch">
|
||||
<a href="{{ lang_url('zh') }}" class="{{ 'active' if lang == 'zh' else '' }}" title="切换到中文">中文</a>
|
||||
<span class="lang-sep">|</span>
|
||||
<a href="{{ lang_url('en') }}" class="{{ 'active' if lang == 'en' else '' }}" title="Switch to English">English</a>
|
||||
</span>
|
||||
<a href="{{ url_for('index') }}">{{ l('价格总览', 'Pricing') }}</a>
|
||||
<a href="{{ url_for('forum_index') }}">{{ l('论坛', 'Forum') }}</a>
|
||||
<a href="{{ url_for('terms_of_service') }}">{{ l('服务条款', 'Terms') }}</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main policy-shell">
|
||||
<article class="policy-card">
|
||||
<h1>{{ l('隐私政策', 'Privacy Policy') }}</h1>
|
||||
<p class="policy-updated">{{ l('最后更新:', 'Last Updated: ') }}{{ updated_on }}</p>
|
||||
<section>
|
||||
<h2>{{ l('1. 我们收集的信息', '1. Information We Collect') }}</h2>
|
||||
<p>{{ l('当你访问本站时,我们会记录基础访问日志(如 IP、浏览器信息、访问时间)用于安全审计与性能优化。注册论坛时,我们会保存你提交的账号信息与帖子内容。', 'When you visit the website, we collect basic access logs (such as IP, browser, and visit time) for security and performance monitoring. If you register in the forum, we store the account details and content you submit.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('2. 信息用途', '2. How We Use Data') }}</h2>
|
||||
<p>{{ l('数据仅用于网站运营、账户管理、内容审核、反垃圾处理、统计分析与服务稳定性保障。', 'Data is used only for site operations, account management, moderation, anti-abuse handling, analytics, and service reliability.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('3. Cookie 与会话', '3. Cookies and Session') }}</h2>
|
||||
<p>{{ l('本站使用必要 Cookie 维持登录状态与语言偏好,不用于出售或跨站追踪个人身份。', 'We use necessary cookies for login sessions and language preferences. They are not used for selling personal data or cross-site identity tracking.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('4. 第三方链接与广告', '4. Third-party Links and Ads') }}</h2>
|
||||
<p>{{ l('页面包含云厂商官网等外部链接,跳转后将受对方隐私政策约束。若启用广告(如 AdSense),第三方可能依据其政策处理展示与测量数据。', 'Pages include external links such as provider websites. Once you leave this site, their privacy policies apply. If ads (e.g., AdSense) are enabled, third parties may process display and measurement data under their own policies.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('5. 数据保留与删除', '5. Data Retention and Deletion') }}</h2>
|
||||
<p>{{ l('论坛内容会在服务运营期间保留。若你希望删除账号或相关数据,可通过页面底部联系方式提交申请。', 'Forum content is retained while the service is operational. You may request account or related data deletion using the contact channel in the footer.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('6. 联系方式', '6. Contact') }}</h2>
|
||||
<p>{{ l('如有隐私问题,请通过 Telegram 联系:', 'For privacy inquiries, contact us via Telegram: ') }}<a href="https://t.me/dockerse" target="_blank" rel="noopener noreferrer">@dockerse</a></p>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>{{ l('仅作采购调研参考 · 请以各云厂商官网实时价格为准', 'For research and shortlisting only. Verify live pricing with provider websites.') }}</p>
|
||||
<nav class="footer-links" aria-label="{{ l('站点链接', 'Site links') }}">
|
||||
<a href="{{ url_for('index') }}">{{ l('首页', 'Home') }}</a>
|
||||
<a href="{{ url_for('forum_index') }}">{{ l('论坛', 'Forum') }}</a>
|
||||
<a href="{{ url_for('terms_of_service') }}">{{ l('服务条款', 'Terms') }}</a>
|
||||
</nav>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
91
templates/terms.html
Normal file
91
templates/terms.html
Normal file
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ 'zh-CN' if lang == 'zh' else 'en' }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ seo.title }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='img/site-logo-mark.svg') }}">
|
||||
<meta name="description" content="{{ seo.description }}">
|
||||
<meta name="robots" content="{{ seo.robots }}">
|
||||
<meta name="theme-color" content="#0F172A">
|
||||
<link rel="canonical" href="{{ seo.canonical_url }}">
|
||||
{% for hreflang, href in seo.alternate_links.items() %}
|
||||
<link rel="alternate" hreflang="{{ hreflang }}" href="{{ href }}">
|
||||
{% endfor %}
|
||||
<meta property="og:type" content="{{ seo.og_type }}">
|
||||
<meta property="og:url" content="{{ seo.og_url }}">
|
||||
<meta property="og:title" content="{{ seo.og_title }}">
|
||||
<meta property="og:description" content="{{ seo.og_description }}">
|
||||
<meta property="og:image" content="{{ seo.og_image }}">
|
||||
<meta name="twitter:card" content="{{ seo.twitter_card }}">
|
||||
<meta name="twitter:title" content="{{ seo.twitter_title }}">
|
||||
<meta name="twitter:description" content="{{ seo.twitter_description }}">
|
||||
<script type="application/ld+json">{{ seo_schema | tojson }}</script>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body class="policy-page">
|
||||
<header class="header">
|
||||
<div class="header-inner">
|
||||
<div class="header-brand">
|
||||
<a href="{{ url_for('index') }}" class="site-logo-link" aria-label="{{ l('云价眼首页', 'VPS Price home') }}">
|
||||
<img src="{{ url_for('static', filename='img/site-logo.svg') }}" alt="{{ l('云价眼 Logo', 'VPS Price Logo') }}" class="site-logo" loading="eager" fetchpriority="high">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="header-nav">
|
||||
<span class="lang-switch">
|
||||
<a href="{{ lang_url('zh') }}" class="{{ 'active' if lang == 'zh' else '' }}" title="切换到中文">中文</a>
|
||||
<span class="lang-sep">|</span>
|
||||
<a href="{{ lang_url('en') }}" class="{{ 'active' if lang == 'en' else '' }}" title="Switch to English">English</a>
|
||||
</span>
|
||||
<a href="{{ url_for('index') }}">{{ l('价格总览', 'Pricing') }}</a>
|
||||
<a href="{{ url_for('forum_index') }}">{{ l('论坛', 'Forum') }}</a>
|
||||
<a href="{{ url_for('privacy_policy') }}">{{ l('隐私政策', 'Privacy') }}</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main policy-shell">
|
||||
<article class="policy-card">
|
||||
<h1>{{ l('服务条款', 'Terms of Service') }}</h1>
|
||||
<p class="policy-updated">{{ l('最后更新:', 'Last Updated: ') }}{{ updated_on }}</p>
|
||||
<section>
|
||||
<h2>{{ l('1. 服务定位', '1. Service Scope') }}</h2>
|
||||
<p>{{ l('本站提供 VPS 公开价格与配置信息聚合,以及社区交流功能,目标是辅助调研与方案初筛,不直接参与交易。', 'This website provides VPS public pricing/spec aggregation and community discussions for research and shortlisting. It does not directly process purchases.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('2. 价格与数据声明', '2. Pricing and Data Disclaimer') }}</h2>
|
||||
<p>{{ l('站点展示的数据可能存在时间差、促销差异或规则变更。任何采购决策前,必须以云厂商官网与正式合同条款为准。', 'Displayed data may include timing delays, promotional differences, or policy changes. Before any procurement decision, verify details against the provider website and official contract terms.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('3. 账户与社区内容', '3. Accounts and Community Content') }}</h2>
|
||||
<p>{{ l('用户需对发布内容负责,不得发布违法、侵权、垃圾广告或恶意攻击信息。站点有权对违规内容执行删除、限制或封禁处理。', 'Users are responsible for posted content and must not publish illegal, infringing, spam, or abusive material. The site may remove content, restrict actions, or ban accounts for violations.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('4. 第三方链接', '4. Third-party Links') }}</h2>
|
||||
<p>{{ l('站内链接可能跳转到第三方网站,相关产品、服务、条款和隐私实践由第三方独立承担。', 'Links may direct you to third-party sites. Their products, services, terms, and privacy practices are managed independently by those third parties.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('5. 责任限制', '5. Limitation of Liability') }}</h2>
|
||||
<p>{{ l('在法律允许范围内,本站不对因使用或依赖站点信息导致的直接或间接损失承担责任。', 'To the extent permitted by law, this site is not liable for direct or indirect losses caused by use of, or reliance on, information provided here.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('6. 条款更新', '6. Updates to Terms') }}</h2>
|
||||
<p>{{ l('我们可能根据运营与合规需要更新本条款,更新后将在页面标注日期并生效。', 'We may update these terms for operational or compliance reasons. Updated versions become effective once posted with a revised date.') }}</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>{{ l('7. 联系方式', '7. Contact') }}</h2>
|
||||
<p>{{ l('如对条款有疑问,请联系:', 'For questions about these terms, contact: ') }}<a href="https://t.me/dockerse" target="_blank" rel="noopener noreferrer">@dockerse</a></p>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>{{ l('仅作采购调研参考 · 请以各云厂商官网实时价格为准', 'For research and shortlisting only. Verify live pricing with provider websites.') }}</p>
|
||||
<nav class="footer-links" aria-label="{{ l('站点链接', 'Site links') }}">
|
||||
<a href="{{ url_for('index') }}">{{ l('首页', 'Home') }}</a>
|
||||
<a href="{{ url_for('forum_index') }}">{{ l('论坛', 'Forum') }}</a>
|
||||
<a href="{{ url_for('privacy_policy') }}">{{ l('隐私政策', 'Privacy') }}</a>
|
||||
</nav>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user