.xml")
+def sitemap_forum_page(lang_code, page):
+ normalized_lang = (lang_code or "").strip().lower()
+ if normalized_lang not in {"zh", "en"}:
+ abort(404)
+ total_pages = _forum_sitemap_total_pages()
+ if page < 1 or page > total_pages:
+ abort(404)
+
+ offset = (page - 1) * SITEMAP_POSTS_PER_FILE
+ rows = (
+ db.session.query(ForumPost.id, ForumPost.updated_at, ForumPost.created_at)
+ .order_by(ForumPost.updated_at.desc(), ForumPost.id.desc())
+ .offset(offset)
+ .limit(SITEMAP_POSTS_PER_FILE)
+ .all()
+ )
+ urls = []
+ for post_id, updated_at, created_at in rows:
+ lastmod = _iso8601_utc(updated_at or created_at)
+ urls.append({
+ "loc": _public_url("forum_post_detail", lang=normalized_lang, post_id=post_id),
+ "changefreq": "weekly",
+ "priority": "0.8",
+ "lastmod": lastmod,
+ "alternates": _sitemap_alternates("forum_post_detail", post_id=post_id),
+ })
+
+ xml = _build_sitemap_urlset_xml(urls)
+ resp = make_response(xml)
+ resp.mimetype = "application/xml"
+ resp.headers["Cache-Control"] = "public, max-age=1800"
return resp
@app.route("/robots.txt")
def robots():
- from flask import make_response
- url = SITE_URL.rstrip("/")
- txt = f"""User-agent: *
+ txt = """User-agent: *
Allow: /
+Allow: /forum/feed.xml
+Disallow: /admin/
+Disallow: /login
+Disallow: /register
+Disallow: /profile
+Disallow: /me
+Disallow: /notifications
+Disallow: /notification/
+Disallow: /forum/post/new
+Disallow: /forum/post/*/edit
+Disallow: /forum/comment/*/edit
+Disallow: /forum/report
+Disallow: /api/
+Disallow: /*?*q=
-Sitemap: {url}/sitemap.xml
-"""
+Sitemap: {}/sitemap.xml
+""".format(_site_root_url())
resp = make_response(txt)
resp.mimetype = "text/plain"
+ resp.headers["Cache-Control"] = "public, max-age=3600"
+ return resp
+
+
+@app.route("/ads.txt")
+def ads_txt():
+ content = (os.environ.get("ADS_TXT_CONTENT") or "").strip()
+ if content:
+ from flask import make_response
+
+ body = content if content.endswith("\n") else "{}\n".format(content)
+ resp = make_response(body)
+ resp.mimetype = "text/plain"
+ resp.headers["Cache-Control"] = "public, max-age=3600"
+ return resp
+ ads_file = os.path.join(app.static_folder or "", "ads.txt")
+ if os.path.isfile(ads_file):
+ return send_from_directory(app.static_folder or "", "ads.txt")
+ from flask import make_response
+
+ resp = make_response("# Configure ADS_TXT_CONTENT or create static/ads.txt\n")
+ resp.mimetype = "text/plain"
+ resp.headers["Cache-Control"] = "public, max-age=600"
return resp
diff --git a/config.py b/config.py
index cf9b588..1f6d598 100644
--- a/config.py
+++ b/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
diff --git a/static/ads.txt b/static/ads.txt
new file mode 100644
index 0000000..5412183
--- /dev/null
+++ b/static/ads.txt
@@ -0,0 +1,2 @@
+# Add your ad network records in the following format:
+# example.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0
diff --git a/static/css/forum.css b/static/css/forum.css
index 564cc40..be315bc 100644
--- a/static/css/forum.css
+++ b/static/css/forum.css
@@ -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 {
diff --git a/static/css/style.css b/static/css/style.css
index 8bef15d..eefc5d0 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -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%;
+ }
+}
diff --git a/static/js/main-simple.js b/static/js/main-simple.js
index 31c16cb..67e36af 100644
--- a/static/js/main-simple.js
+++ b/static/js/main-simple.js
@@ -318,7 +318,7 @@
'' + plan.traffic + ' ' +
'' + displayPrice + ' ' +
'' +
- '' + btnText + ' ' +
+ '' + btnText + ' ' +
' ';
return tr;
diff --git a/templates/auth/login.html b/templates/auth/login.html
index 2b41af3..5b83849 100644
--- a/templates/auth/login.html
+++ b/templates/auth/login.html
@@ -3,6 +3,7 @@
+
{{ l('用户登录', 'Login') }} - {{ l('云价眼', 'VPS Price') }}
diff --git a/templates/auth/register.html b/templates/auth/register.html
index a39f980..a49020b 100644
--- a/templates/auth/register.html
+++ b/templates/auth/register.html
@@ -3,6 +3,7 @@
+
{{ l('用户注册', 'Register') }} - {{ l('云价眼', 'VPS Price') }}
diff --git a/templates/forum/comment_form.html b/templates/forum/comment_form.html
index a6d7ba7..0f8ca75 100644
--- a/templates/forum/comment_form.html
+++ b/templates/forum/comment_form.html
@@ -3,6 +3,7 @@
+
{{ l('编辑评论', 'Edit Comment') }} - {{ l('论坛', 'Forum') }}
diff --git a/templates/forum/index.html b/templates/forum/index.html
index 75a46f2..f4e2a63 100644
--- a/templates/forum/index.html
+++ b/templates/forum/index.html
@@ -3,8 +3,35 @@
- {{ l('论坛', 'Forum') }} - {{ l('云价眼', 'VPS Price') }}
+ {{ seo.title if seo else (l('论坛', 'Forum') ~ ' - ' ~ l('云价眼', 'VPS Price')) }}
+ {% if seo %}
+
+
+
+
+
+ {% if seo.prev_canonical_url %} {% endif %}
+ {% if seo.next_canonical_url %} {% endif %}
+ {% for hreflang, href in seo.alternate_links.items() %}
+
+ {% endfor %}
+ {% if seo.feed_url %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+ {% if seo_schema %}
+
+ {% endif %}
+ {% endif %}
@@ -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': []} %}
{{ t.tagline }}
{{ t.disclaimer }}
+ +{{ t.faq_title }}
+{{ t.faq_intro }}
+{{ item.question }}
+{{ item.answer }}
+{{ t.cta_title }}
+{{ t.cta_lede }}
+{{ t.footer_note }}
+{{ t.contact_label }}
@@ -236,6 +292,6 @@ // 首屏直出数据,避免等待 /api/plans 再渲染表格,加快首屏 window.__INITIAL_PLANS__ = {{ initial_plans_json|tojson }}; - + diff --git a/templates/privacy.html b/templates/privacy.html new file mode 100644 index 0000000..dfb2255 --- /dev/null +++ b/templates/privacy.html @@ -0,0 +1,87 @@ + + + + + +{{ l('隐私政策', 'Privacy Policy') }}
+{{ l('最后更新:', 'Last Updated: ') }}{{ updated_on }}
+{{ l('1. 我们收集的信息', '1. Information We Collect') }}
+{{ 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.') }}
+{{ l('2. 信息用途', '2. How We Use Data') }}
+{{ l('数据仅用于网站运营、账户管理、内容审核、反垃圾处理、统计分析与服务稳定性保障。', 'Data is used only for site operations, account management, moderation, anti-abuse handling, analytics, and service reliability.') }}
+{{ l('3. Cookie 与会话', '3. Cookies and Session') }}
+{{ 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.') }}
+{{ l('4. 第三方链接与广告', '4. Third-party Links and Ads') }}
+{{ 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.') }}
+{{ l('5. 数据保留与删除', '5. Data Retention and Deletion') }}
+{{ 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.') }}
+{{ l('6. 联系方式', '6. Contact') }}
+{{ l('如有隐私问题,请通过 Telegram 联系:', 'For privacy inquiries, contact us via Telegram: ') }}@dockerse
+{{ l('服务条款', 'Terms of Service') }}
+{{ l('最后更新:', 'Last Updated: ') }}{{ updated_on }}
+{{ l('1. 服务定位', '1. Service Scope') }}
+{{ l('本站提供 VPS 公开价格与配置信息聚合,以及社区交流功能,目标是辅助调研与方案初筛,不直接参与交易。', 'This website provides VPS public pricing/spec aggregation and community discussions for research and shortlisting. It does not directly process purchases.') }}
+{{ l('2. 价格与数据声明', '2. Pricing and Data Disclaimer') }}
+{{ 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.') }}
+{{ l('3. 账户与社区内容', '3. Accounts and Community Content') }}
+{{ 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.') }}
+{{ l('4. 第三方链接', '4. Third-party Links') }}
+{{ l('站内链接可能跳转到第三方网站,相关产品、服务、条款和隐私实践由第三方独立承担。', 'Links may direct you to third-party sites. Their products, services, terms, and privacy practices are managed independently by those third parties.') }}
+{{ l('5. 责任限制', '5. Limitation of Liability') }}
+{{ 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.') }}
+{{ l('6. 条款更新', '6. Updates to Terms') }}
+{{ l('我们可能根据运营与合规需要更新本条款,更新后将在页面标注日期并生效。', 'We may update these terms for operational or compliance reasons. Updated versions become effective once posted with a revised date.') }}
+{{ l('7. 联系方式', '7. Contact') }}
+{{ l('如对条款有疑问,请联系:', 'For questions about these terms, contact: ') }}@dockerse
+