feat: OpsxAPI v1.0 全协议接口工作台
- 六协议调试:HTTP/GraphQL/WebSocket/SSE/Socket.IO/MQTT
- 集合树/多标签/多环境变量/历史记录,localStorage 持久化
- 导入:OpenAPI 2/3/3.1、Postman、Apifox、Apipost、cURL、HAR、
Insomnia、Hoppscotch、Markdown(自动识别,21 个单测覆盖)
- 导出:OpenAPI 3.0 JSON/YAML、Postman v2.1、Markdown 文档、
全量备份;单请求生成 cURL/fetch/Python/Go 代码
- Gin 服务:HTTP 代理、NDJSON 流式代理(SSE 透传)、
WebSocket 隧道(浏览器自定义握手 Header)
- 内置产品官网 /home 与用户文档站 /docs(SSR,随二进制交付)
- lucide 图标体系 + 品牌标识(Logo/favicon),明暗双主题
- 环境变量:彩色徽标切换器、管理弹窗、{{ 自动补全
This commit is contained in:
commit
dec66db5aa
182 files changed
+23377
No files matched your search
@@ -0,0 +1,282 @@
|
||||
/* OpsxAPI 官网 —— 营销单页样式 */
|
||||
:root {
|
||||
--blue: #2563eb;
|
||||
--blue-dark: #1d4ed8;
|
||||
--blue-soft: rgba(37, 99, 235, 0.08);
|
||||
--bg: #ffffff;
|
||||
--bg-alt: #f6f8fc;
|
||||
--border: #e4e9f1;
|
||||
--text: #111827;
|
||||
--text-2: #5b6880;
|
||||
--text-3: #98a2b3;
|
||||
--green: #059669;
|
||||
--amber: #d97706;
|
||||
--radius: 14px;
|
||||
--shadow: 0 1px 3px rgba(16,24,40,.06), 0 12px 32px rgba(16,24,40,.07);
|
||||
--mono: "JetBrains Mono", SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
* { box-sizing: border-box; scroll-behavior: smooth; }
|
||||
body {
|
||||
margin: 0; font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif;
|
||||
color: var(--text); background: var(--bg); font-size: 15px; line-height: 1.75;
|
||||
}
|
||||
a { color: var(--blue); text-decoration: none; }
|
||||
h1,h2,h3 { letter-spacing: -0.015em; }
|
||||
section[id] { scroll-margin-top: 76px; }
|
||||
.wrap { max-width: 1120px; margin: 0 auto; padding: 0 24px; }
|
||||
.wrap.narrow { max-width: 820px; }
|
||||
/* ---------- SVG 图标(lucide sprite) ---------- */
|
||||
.i { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; vertical-align: -2px; }
|
||||
.i-lg { width: 28px; height: 28px; vertical-align: middle; }
|
||||
code { font-family: var(--mono); font-size: 13px; background: var(--blue-soft); color: var(--blue-dark); border-radius: 5px; padding: 1px 6px; }
|
||||
|
||||
/* ---------- 按钮 ---------- */
|
||||
.btn {
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
||||
border-radius: 10px; padding: 9px 20px; font-size: 14.5px; font-weight: 600; cursor: pointer;
|
||||
transition: all .18s; border: 1px solid transparent; white-space: nowrap;
|
||||
}
|
||||
.btn-lg { padding: 12px 26px; font-size: 15.5px; }
|
||||
.btn-primary { background: var(--blue); color: #fff !important; box-shadow: 0 6px 18px rgba(37,99,235,.28); }
|
||||
.btn-primary:hover { background: var(--blue-dark); transform: translateY(-1px); }
|
||||
.btn-ghost { border-color: var(--border); color: var(--text) !important; background: #fff; }
|
||||
.btn-ghost:hover { border-color: var(--blue); color: var(--blue) !important; }
|
||||
.btn-inverse { background: #fff; color: var(--blue) !important; }
|
||||
.btn-inverse:hover { transform: translateY(-1px); }
|
||||
.btn-outline { border-color: rgba(255,255,255,.5); color: #fff !important; }
|
||||
.btn-outline:hover { border-color: #fff; background: rgba(255,255,255,.1); }
|
||||
|
||||
/* ---------- 导航 ---------- */
|
||||
.nav {
|
||||
position: sticky; top: 0; z-index: 50; background: rgba(255,255,255,.9);
|
||||
backdrop-filter: blur(10px); border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.nav-inner { max-width: 1120px; margin: 0 auto; padding: 0 24px; height: 60px; display: flex; align-items: center; gap: 28px; }
|
||||
.brand { display: flex; align-items: center; gap: 9px; }
|
||||
.brand-logo { display: inline-flex; }
|
||||
.brand-logo svg { display: block; border-radius: 8px; }
|
||||
.brand-name { font-size: 17px; font-weight: 800; color: var(--text) !important; }
|
||||
.nav-links { display: flex; gap: 22px; }
|
||||
.nav-links a { font-size: 14px; font-weight: 500; color: var(--text-2) !important; }
|
||||
.nav-links a:hover { color: var(--blue) !important; }
|
||||
.nav-cta { margin-left: auto; }
|
||||
|
||||
/* ---------- Hero ---------- */
|
||||
.hero {
|
||||
position: relative; overflow: hidden;
|
||||
background:
|
||||
radial-gradient(1200px 500px at 85% -10%, rgba(37,99,235,.14), transparent 60%),
|
||||
radial-gradient(800px 400px at -10% 10%, rgba(5,150,105,.08), transparent 55%),
|
||||
linear-gradient(180deg, #f8faff 0%, #fff 100%);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.hero-inner {
|
||||
max-width: 1120px; margin: 0 auto; padding: 72px 24px 64px;
|
||||
display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: 48px; align-items: center;
|
||||
}
|
||||
.hero-badge {
|
||||
display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--blue-dark);
|
||||
background: #fff; border: 1px solid rgba(37,99,235,.25); border-radius: 999px; padding: 4px 14px;
|
||||
box-shadow: 0 2px 8px rgba(37,99,235,.1); margin-bottom: 18px;
|
||||
}
|
||||
.hero h1 { font-size: 44px; line-height: 1.22; font-weight: 800; margin: 0 0 16px; }
|
||||
.hero h1 em { font-style: normal; color: var(--blue); }
|
||||
.hero-sub { color: var(--text-2); font-size: 16px; margin: 0 0 26px; }
|
||||
.hero-actions { display: flex; gap: 14px; flex-wrap: wrap; }
|
||||
.hero-actions.center { justify-content: center; }
|
||||
.hero-pills { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; }
|
||||
.hero-pills span {
|
||||
font-size: 12px; color: var(--text-2); background: #fff; border: 1px solid var(--border);
|
||||
padding: 3px 12px; border-radius: 999px;
|
||||
}
|
||||
|
||||
/* ---------- Mock 界面 ---------- */
|
||||
.mock {
|
||||
border-radius: 14px; overflow: hidden; box-shadow: var(--shadow); border: 1px solid #1e2b45;
|
||||
background: #0d1526; transform: perspective(1400px) rotateY(-4deg) rotateX(1.5deg);
|
||||
}
|
||||
.mock-titlebar {
|
||||
display: flex; align-items: center; gap: 7px; height: 34px; padding: 0 12px;
|
||||
background: #101a30; border-bottom: 1px solid #1e2b45;
|
||||
}
|
||||
.mock-titlebar i { width: 11px; height: 11px; border-radius: 50%; background: #3a4a6b; }
|
||||
.mock-titlebar i:nth-child(1) { background: #f87171; } .mock-titlebar i:nth-child(2) { background: #fbbf24; } .mock-titlebar i:nth-child(3) { background: #34d399; }
|
||||
.mock-titlebar span { margin-left: 8px; color: #8b99b0; font-size: 11px; }
|
||||
.mock-body { display: grid; grid-template-columns: 158px minmax(0,1fr); min-height: 320px; }
|
||||
.mock-side { border-right: 1px solid #1e2b45; padding: 10px 8px; background: #0c1322; }
|
||||
.mock-search { height: 22px; border-radius: 6px; background: #16213a; margin-bottom: 10px; }
|
||||
.mock-group { color: #8b99b0; font-size: 11px; padding: 6px 6px 3px; }
|
||||
.mock-item { display: flex; align-items: center; gap: 6px; color: #aab8d0; font-size: 11.5px; padding: 4px 6px; border-radius: 6px; }
|
||||
.mock-item.active { background: rgba(37,99,235,.18); color: #93c5fd; }
|
||||
.mi { width: 11px; height: 11px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; opacity: .8; }
|
||||
.m-get { color: #34d399; font-style: normal; font-size: 9.5px; min-width: 30px; }
|
||||
.m-post { color: #fbbf24; font-style: normal; font-size: 9.5px; min-width: 30px; }
|
||||
.mock-main { padding: 10px 12px; display: flex; flex-direction: column; gap: 9px; }
|
||||
.mock-urlbar { display: flex; align-items: center; gap: 8px; }
|
||||
.mock-url {
|
||||
flex: 1; background: #0a1120; border: 1px solid #22304f; border-radius: 7px; color: #cbd5e1;
|
||||
font-family: var(--mono); font-size: 11px; padding: 6px 10px;
|
||||
}
|
||||
.mock-send {
|
||||
background: var(--blue); color: #fff; font-size: 11px; font-weight: 600;
|
||||
border-radius: 7px; padding: 6px 14px;
|
||||
}
|
||||
.mock-req, .mock-resp { border: 1px solid #1e2b45; border-radius: 9px; padding: 8px 10px; background: #0c1322; }
|
||||
.mock-req .tab, .mock-resp .tab { color: #5c6a83; font-size: 10.5px; margin-right: 12px; }
|
||||
.mock-req .tab.on { color: #93c5fd; border-bottom: 2px solid var(--blue); padding-bottom: 2px; }
|
||||
.mock-resp .tab.on { color: #6ee7b7; border-bottom: 2px solid var(--green); padding-bottom: 2px; }
|
||||
.mock-status { float: right; color: #34d399; font-size: 10px; font-family: var(--mono); }
|
||||
.mock pre {
|
||||
margin: 8px 0 2px; font-family: var(--mono); font-size: 11px; line-height: 1.6; color: #cbd5e1;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.mock pre i { font-style: normal; color: #7dd3fc; }
|
||||
.mock pre u { text-decoration: none; color: #86efac; }
|
||||
|
||||
/* ---------- 通用 Section ---------- */
|
||||
.section { padding: 72px 0; }
|
||||
.section.alt { background: var(--bg-alt); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
|
||||
.sec-title { font-size: 30px; font-weight: 800; text-align: center; margin: 0 0 8px; }
|
||||
.sec-sub { text-align: center; color: var(--text-2); margin: 0 0 40px; }
|
||||
.grid { display: grid; gap: 18px; }
|
||||
.g3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
|
||||
.g4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
|
||||
|
||||
/* ---------- 卡片 ---------- */
|
||||
.card {
|
||||
background: #fff; border: 1px solid var(--border); border-radius: var(--radius);
|
||||
padding: 22px; box-shadow: 0 1px 2px rgba(16,24,40,.04); transition: all .18s;
|
||||
}
|
||||
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow); border-color: rgba(37,99,235,.35); }
|
||||
.card h3 { font-size: 16px; margin: 0 0 6px; }
|
||||
.card p { color: var(--text-2); font-size: 13.5px; margin: 0; }
|
||||
.card-icon { color: var(--blue); margin-bottom: 10px; }
|
||||
.card-icon .i-lg { width: 30px; height: 30px; }
|
||||
.card.row { display: flex; gap: 14px; align-items: flex-start; }
|
||||
.card.row .card-icon { margin: 2px 0 0; }
|
||||
.card.mini h3 { font-size: 15px; display: flex; align-items: center; gap: 8px; }
|
||||
.card.mini h3 .i-lg { width: 20px; height: 20px; color: var(--blue); }
|
||||
.card.mini p { font-size: 13px; }
|
||||
|
||||
/* ---------- 协议 ---------- */
|
||||
.proto {
|
||||
background: #fff; border: 1px solid var(--border); border-left: 3px solid var(--blue);
|
||||
border-radius: 12px; padding: 18px 20px;
|
||||
}
|
||||
.proto h3 { margin: 0 0 6px; font-size: 15.5px; display: flex; align-items: center; gap: 8px; }
|
||||
.proto h3 .i { color: var(--blue); width: 17px; height: 17px; }
|
||||
.proto p { margin: 0; color: var(--text-2); font-size: 13.5px; }
|
||||
|
||||
/* ---------- 导入导出 ---------- */
|
||||
.badges { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-bottom: 18px; }
|
||||
.badges span {
|
||||
background: #fff; border: 1px solid var(--border); border-radius: 999px;
|
||||
padding: 6px 16px; font-size: 13px; font-weight: 500; color: var(--text);
|
||||
}
|
||||
.badges span.more { border-style: dashed; color: var(--text-2); }
|
||||
.footnote { text-align: center; color: var(--text-3); font-size: 12.5px; margin: 10px 0 0; }
|
||||
.exchange-actions { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; margin-top: 26px; }
|
||||
.chip {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: #fff; border: 1px solid var(--border); border-radius: 10px; padding: 8px 16px;
|
||||
font-size: 13px; font-weight: 600; color: var(--text-2);
|
||||
}
|
||||
.chip.big { background: var(--blue); border-color: var(--blue); color: #fff; padding: 10px 22px; }
|
||||
.exchange-arrow { color: var(--text-3); font-size: 15px; }
|
||||
|
||||
/* ---------- 对比表 ---------- */
|
||||
.table-wrap { overflow-x: auto; }
|
||||
.cmp { border-collapse: collapse; width: 100%; background: #fff; border-radius: 12px; overflow: hidden; box-shadow: 0 0 0 1px var(--border); font-size: 14px; }
|
||||
.cmp th, .cmp td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--border); }
|
||||
.cmp th { background: #f9fbfe; font-size: 13.5px; }
|
||||
.cmp tr:last-child td { border-bottom: none; }
|
||||
.cmp td.hl { background: rgba(37,99,235,.05); color: var(--blue-dark); }
|
||||
.cmp th.hl { background: var(--blue); color: #fff; }
|
||||
|
||||
/* ---------- 定价 ---------- */
|
||||
.pricing { display: grid; grid-template-columns: repeat(2, minmax(0, 380px)); gap: 22px; justify-content: center; }
|
||||
.pricing.three { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: 1080px; margin: 0 auto; }
|
||||
.price-card {
|
||||
position: relative; background: #fff; border: 1px solid var(--border); border-radius: 16px;
|
||||
padding: 30px 28px 26px; box-shadow: 0 1px 2px rgba(16,24,40,.04); display: flex; flex-direction: column;
|
||||
}
|
||||
.price-card.featured { border: 2px solid var(--blue); box-shadow: 0 12px 36px rgba(37,99,235,.16); }
|
||||
.price-card.enterprise {
|
||||
background: linear-gradient(160deg, #101a30 0%, #0d1526 70%);
|
||||
border: 1px solid #2a3b5e; box-shadow: 0 14px 40px rgba(13,21,38,.4);
|
||||
}
|
||||
.price-card.enterprise .plan { color: #8b99b0; }
|
||||
.price-card.enterprise .price b { color: #fff; font-size: 36px; }
|
||||
.price-card.enterprise .price span { color: #8b99b0; }
|
||||
.price-card.enterprise .plan-sub { color: #8b99b0; }
|
||||
.price-card.enterprise li { color: #aab8d0; border-bottom-color: #22304f; }
|
||||
.price-card.enterprise .deal-note {
|
||||
margin-top: 14px; font-size: 11.5px; line-height: 1.7; color: #8b99b0;
|
||||
background: rgba(255,255,255,.05); border: 1px dashed #2a3b5e; border-radius: 9px; padding: 9px 12px;
|
||||
}
|
||||
.price-card.enterprise .deal-note b { color: #93c5fd; }
|
||||
.btn-dark { background: #fff; color: #0d1526 !important; }
|
||||
.btn-dark:hover { transform: translateY(-1px); background: #e8eefc; }
|
||||
.plan-tag {
|
||||
position: absolute; top: -13px; left: 50%; transform: translateX(-50%);
|
||||
background: var(--blue); color: #fff; font-size: 12px; font-weight: 700;
|
||||
padding: 3px 14px; border-radius: 999px; white-space: nowrap;
|
||||
}
|
||||
.plan-tag.dark { background: #0d1526; border: 1px solid #2a3b5e; }
|
||||
.plan { font-size: 16px; font-weight: 700; color: var(--text-2); }
|
||||
.price { margin: 8px 0 2px; }
|
||||
.price b { font-size: 44px; font-weight: 800; letter-spacing: -.02em; color: var(--text); }
|
||||
.price span { color: var(--text-3); font-size: 14px; margin-left: 4px; }
|
||||
.plan-sub { color: var(--text-3); font-size: 13px; margin-bottom: 18px; }
|
||||
.price-card ul { list-style: none; padding: 0; margin: 0 0 22px; flex: 1; }
|
||||
.price-card li { font-size: 13.5px; color: var(--text-2); padding: 6px 0; border-bottom: 1px dashed var(--border); }
|
||||
.price-card li:last-child { border-bottom: none; }
|
||||
.price-card .btn { width: 100%; }
|
||||
.growth-title { text-align: center; font-size: 20px; font-weight: 700; margin: 52px 0 22px; }
|
||||
|
||||
/* ---------- FAQ ---------- */
|
||||
.faq details {
|
||||
background: #fff; border: 1px solid var(--border); border-radius: 12px;
|
||||
padding: 16px 20px; margin-bottom: 12px;
|
||||
}
|
||||
.faq summary { font-weight: 600; cursor: pointer; font-size: 15px; list-style: none; display: flex; justify-content: space-between; }
|
||||
.faq summary::after { content: "+"; color: var(--text-3); font-weight: 400; }
|
||||
.faq details[open] summary::after { content: "-"; }
|
||||
.faq details p { margin: 10px 0 0; color: var(--text-2); font-size: 14px; }
|
||||
|
||||
/* ---------- CTA ---------- */
|
||||
.cta {
|
||||
background: linear-gradient(135deg, #1d4ed8 0%, #2563eb 55%, #3b82f6 100%);
|
||||
color: #fff; text-align: center; padding: 72px 24px;
|
||||
}
|
||||
.cta h2 { font-size: 32px; font-weight: 800; margin: 0 0 10px; }
|
||||
.cta p { color: rgba(255,255,255,.85); margin: 0 0 28px; }
|
||||
|
||||
/* ---------- Footer ---------- */
|
||||
.footer { background: #0d1526; color: #8b99b0; padding: 52px 0 0; font-size: 13.5px; }
|
||||
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 32px; padding-bottom: 36px; }
|
||||
.footer .brand-name { color: #fff !important; }
|
||||
.footer .f-logo { display: block; border-radius: 8px; }
|
||||
.footer .brand { display: flex; align-items: center; gap: 9px; }
|
||||
.footer-desc { margin: 12px 0 0; font-size: 13px; line-height: 1.8; }
|
||||
.footer h4 { color: #fff; font-size: 13.5px; margin: 4px 0 12px; }
|
||||
.footer a { display: block; color: #8b99b0 !important; padding: 4px 0; font-size: 13px; }
|
||||
.footer a:hover { color: #fff !important; }
|
||||
.footer-bar { border-top: 1px solid #1e2b45; text-align: center; padding: 18px; font-size: 12px; color: #5c6a83; }
|
||||
|
||||
/* ---------- 响应式 ---------- */
|
||||
@media (max-width: 1020px) {
|
||||
.hero-inner { grid-template-columns: 1fr; padding-top: 52px; }
|
||||
.mock { transform: none; }
|
||||
.g4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
|
||||
.g3 { grid-template-columns: repeat(2, minmax(0,1fr)); }
|
||||
.pricing, .pricing.three { grid-template-columns: minmax(0, 420px); }
|
||||
.footer-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
@media (max-width: 680px) {
|
||||
.nav-links { display: none; }
|
||||
.hero h1 { font-size: 32px; }
|
||||
.sec-title { font-size: 24px; }
|
||||
.g3, .g4 { grid-template-columns: 1fr; }
|
||||
.footer-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Package site 在 /home(及 /)提供产品官网单页(SSR,资源嵌入二进制)。
|
||||
package site
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
//go:embed template.html assets
|
||||
var files embed.FS
|
||||
|
||||
// Handler 官网服务
|
||||
type Handler struct {
|
||||
html string
|
||||
}
|
||||
|
||||
func New() *Handler {
|
||||
raw, err := files.ReadFile("template.html")
|
||||
if err != nil {
|
||||
panic("site: template.html 缺失: " + err.Error())
|
||||
}
|
||||
return &Handler{html: string(raw)}
|
||||
}
|
||||
|
||||
// Register 注册路由:/ 与 /home 均渲染官网
|
||||
func (h *Handler) Register(r *gin.Engine) {
|
||||
r.GET("/", h.Page)
|
||||
r.GET("/home", h.Page)
|
||||
r.GET("/index.html", h.Page)
|
||||
r.GET("/site/assets/*file", h.Asset)
|
||||
}
|
||||
|
||||
// Page 渲染官网
|
||||
func (h *Handler) Page(c *gin.Context) {
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
c.Header("Cache-Control", "no-cache")
|
||||
_, _ = c.Writer.WriteString(h.html)
|
||||
}
|
||||
|
||||
// Asset 静态资源
|
||||
func (h *Handler) Asset(c *gin.Context) {
|
||||
name := strings.TrimPrefix(c.Param("file"), "/")
|
||||
if strings.Contains(name, "..") {
|
||||
c.Status(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
data, err := files.ReadFile("assets/" + name)
|
||||
if err != nil {
|
||||
c.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case strings.HasSuffix(name, ".css"):
|
||||
c.Header("Content-Type", "text/css; charset=utf-8")
|
||||
case strings.HasSuffix(name, ".js"):
|
||||
c.Header("Content-Type", "application/javascript; charset=utf-8")
|
||||
case strings.HasSuffix(name, ".svg"):
|
||||
c.Header("Content-Type", "image/svg+xml")
|
||||
default:
|
||||
c.Header("Content-Type", "application/octet-stream")
|
||||
}
|
||||
c.Header("Cache-Control", "public, max-age=3600")
|
||||
c.Data(http.StatusOK, "", data)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package site
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func newTestRouter(t *testing.T) *gin.Engine {
|
||||
t.Helper()
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
New().Register(r)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestSitePageRender(t *testing.T) {
|
||||
r := newTestRouter(t)
|
||||
for _, path := range []string{"/", "/home", "/index.html"} {
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, path, nil))
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("%s: expect 200, got %d", path, w.Code)
|
||||
}
|
||||
body := w.Body.String()
|
||||
for _, want := range []string{
|
||||
"<title>OpsxAPI", // 标题
|
||||
`id="features"`, // 功能区
|
||||
`id="pricing"`, // 定价区
|
||||
"¥99", // 入门价
|
||||
"¥8,000", // 企业档标价
|
||||
"¥30,000", // 买断标价
|
||||
"¥5,000–8,000", // 成交区间
|
||||
"永久买断", // 买断 FAQ
|
||||
"早鸟创始价", // 让利策略
|
||||
"Socket.IO", // 协议覆盖
|
||||
"hello@opsxapi.dev", // 联系方式
|
||||
`href="/docs"`, // 文档互链
|
||||
`href="/site/assets/site.css"`, // 样式资源
|
||||
} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Fatalf("%s: missing %q", path, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSiteAsset(t *testing.T) {
|
||||
r := newTestRouter(t)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/site/assets/site.css", nil))
|
||||
if w.Code != http.StatusOK || !strings.Contains(w.Header().Get("Content-Type"), "text/css") {
|
||||
t.Fatalf("css: %d %s", w.Code, w.Header().Get("Content-Type"))
|
||||
}
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/site/assets/../template.html", nil))
|
||||
if w.Code != http.StatusBadRequest && w.Code != http.StatusNotFound {
|
||||
t.Fatalf("path traversal should be rejected, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>OpsxAPI — 本地优先的全协议接口工作台</title>
|
||||
<meta name="description" content="OpsxAPI:HTTP、GraphQL、WebSocket、SSE、Socket.IO、MQTT 一站式调试。本地优先数据主权,OpenAPI/Postman/cURL 全格式导入导出。个人版 ¥99/年,团队版 ¥99/人/年。">
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%232563eb'/%3E%3Ctext x='16' y='22' font-size='18' font-weight='bold' fill='%23fff' text-anchor='middle' font-family='sans-serif'%3EO%3C/text%3E%3C/svg%3E">
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22g%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%2264%22%20y2%3D%2264%22%20gradientUnits%3D%22userSpaceOnUse%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%232563EB%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2306B6D4%22%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22url%28%23g%29%22%2F%3E%3Cg%20stroke%3D%22%23fff%22%20stroke-width%3D%225%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M17%2025h26%22%2F%3E%3Cpath%20d%3D%22M37%2019l6%206-6%206%22%2F%3E%3Cpath%20d%3D%22M47%2039H21%22%2F%3E%3Cpath%20d%3D%22M27%2033l-6%206%206%206%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
|
||||
<link rel="stylesheet" href="/site/assets/site.css">
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
||||
<symbol id="i-rocket" viewBox="0 0 24 24"><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /> <path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09" /> <path d="M9 12a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.4 22.4 0 0 1-4 2z" /> <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 .05 5 .05" /></symbol>
|
||||
<symbol id="i-shield-check" viewBox="0 0 24 24"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /> <path d="m9 12 2 2 4-4" /></symbol>
|
||||
<symbol id="i-arrow-left-right" viewBox="0 0 24 24"><path d="M8 3 4 7l4 4" /> <path d="M4 7h16" /> <path d="m16 21 4-4-4-4" /> <path d="M20 17H4" /></symbol>
|
||||
<symbol id="i-plug-zap" viewBox="0 0 24 24"><path d="M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z" /> <path d="m2 22 3-3" /> <path d="M7.5 13.5 10 11" /> <path d="M10.5 16.5 13 14" /> <path d="m18 3-4 4h6l-4 4" /></symbol>
|
||||
<symbol id="i-package" viewBox="0 0 24 24"><path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z" /> <path d="M12 22V12" /> <polyline points="3.29 7 12 12 20.71 7" /> <path d="m7.5 4.27 9 5.15" /></symbol>
|
||||
<symbol id="i-folder-tree" viewBox="0 0 24 24"><path d="M20 10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2.5a1 1 0 0 1-.8-.4l-.9-1.2A1 1 0 0 0 15 3h-2a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z" /> <path d="M20 21a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1h-2.9a1 1 0 0 1-.88-.55l-.42-.85a1 1 0 0 0-.92-.6H13a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z" /> <path d="M3 5a2 2 0 0 0 2 2h3" /> <path d="M3 3v13a2 2 0 0 0 2 2h3" /></symbol>
|
||||
<symbol id="i-globe" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> <path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" /> <path d="M2 12h20" /></symbol>
|
||||
<symbol id="i-hexagon" viewBox="0 0 24 24"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" /></symbol>
|
||||
<symbol id="i-zap" viewBox="0 0 24 24"><path d="M15.914 4a1.5 1.5 0 00-2.474-1.561l-9 9A1.5 1.5 0 005.5 14h4.002a.5.5 0 01.471.666L8.086 20a1.5 1.5 0 002.475 1.56l9-9A1.5 1.5 0 0018.5 10h-3.997a.5.5 0 01-.472-.667z" /></symbol>
|
||||
<symbol id="i-radio" viewBox="0 0 24 24"><path d="M16.247 7.761a6 6 0 0 1 0 8.478" /> <path d="M19.075 4.933a10 10 0 0 1 0 14.134" /> <path d="M4.925 19.067a10 10 0 0 1 0-14.134" /> <path d="M7.753 16.239a6 6 0 0 1 0-8.478" /> <circle cx="12" cy="12" r="2" /></symbol>
|
||||
<symbol id="i-mailbox" viewBox="0 0 24 24"><path d="M22 17a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9.5C2 7 4 5 6.5 5H18c2.2 0 4 1.8 4 4v8Z" /> <polyline points="15,9 18,9 18,11" /> <path d="M6.5 5C9 5 11 7 11 9.5V17a2 2 0 0 1-2 2" /> <line x1="6" x2="7" y1="10" y2="10" /></symbol>
|
||||
<symbol id="i-code" viewBox="0 0 24 24"><path d="m16 18 6-6-6-6" /> <path d="m8 6-6 6 6 6" /></symbol>
|
||||
<symbol id="i-book-open" viewBox="0 0 24 24"><path d="M12 5v16" /> <path d="M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z" /></symbol>
|
||||
<symbol id="i-sprout" viewBox="0 0 24 24"><path d="M14 9.536V7a4 4 0 0 1 4-4h1.5a.5.5 0 0 1 .5.5V5a4 4 0 0 1-4 4 4 4 0 0 0-4 4c0 2 1 3 1 5a5 5 0 0 1-1 3" /> <path d="M4 9a5 5 0 0 1 8 4 5 5 0 0 1-8-4" /> <path d="M5 21h14" /></symbol>
|
||||
<symbol id="i-graduation-cap" viewBox="0 0 24 24"><path d="M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z" /> <path d="M22 10v6" /> <path d="M6 12.5V16a6 3 0 0 0 12 0v-3.5" /></symbol>
|
||||
<symbol id="i-credit-card" viewBox="0 0 24 24"><rect width="20" height="14" x="2" y="5" rx="2" /> <line x1="2" x2="22" y1="10" y2="10" /> <path d="M6 14h2" /></symbol>
|
||||
<symbol id="i-megaphone" viewBox="0 0 24 24"><path d="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z" /> <path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14" /> <path d="M8 6v8" /></symbol>
|
||||
<symbol id="i-download" viewBox="0 0 24 24"><path d="M12 15V3" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="m7 10 5 5 5-5" /></symbol>
|
||||
<symbol id="i-upload" viewBox="0 0 24 24"><path d="M12 3v12" /> <path d="m17 8-5-5-5 5" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></symbol>
|
||||
<symbol id="i-library" viewBox="0 0 24 24"><path d="m16 6 4 14" /> <path d="M12 6v14" /> <path d="M8 8v12" /> <path d="M4 4v16" /></symbol>
|
||||
</svg>
|
||||
|
||||
<!-- ===================== 导航 ===================== -->
|
||||
<header class="nav">
|
||||
<div class="nav-inner">
|
||||
<a class="brand" href="/home">
|
||||
<span class="brand-logo"><svg width="30" height="30" viewBox="0 0 64 64" aria-label="OpsxAPI"><defs><linearGradient id="lg-site" x1="0" y1="0" x2="64" y2="64" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2563EB"/><stop offset="1" stop-color="#06B6D4"/></linearGradient></defs><rect width="64" height="64" rx="14" fill="url(#lg-site)"/><g stroke="#fff" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" fill="none"><path d="M17 25h26"/><path d="M37 19l6 6-6 6"/><path d="M47 39H21"/><path d="M27 33l-6 6 6 6"/></g></svg></span>
|
||||
<span class="brand-name">OpsxAPI</span>
|
||||
</a>
|
||||
<nav class="nav-links">
|
||||
<a href="#features">功能</a>
|
||||
<a href="#protocols">协议</a>
|
||||
<a href="#exchange">导入导出</a>
|
||||
<a href="#pricing">定价</a>
|
||||
<a href="/docs" target="_blank" rel="noopener">文档</a>
|
||||
</nav>
|
||||
<div class="nav-cta">
|
||||
<a class="btn btn-primary" href="/" target="_blank" rel="noopener">打开工作台 ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- ===================== Hero ===================== -->
|
||||
<section class="hero">
|
||||
<div class="hero-inner">
|
||||
<div class="hero-copy">
|
||||
<div class="hero-badge"><svg class="i"><use href="#i-rocket"/></svg> v1.0 正式发布 · 数据 100% 本地</div>
|
||||
<h1>一个工作台,<br>调完<em>六种协议</em>的接口</h1>
|
||||
<p class="hero-sub">
|
||||
HTTP · GraphQL · WebSocket · SSE · Socket.IO · MQTT。<br>
|
||||
集合管理、多环境变量、全格式导入导出、代码生成、内置文档站 —— 运维与开发的自托管接口利器。
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="btn btn-primary btn-lg" href="/" target="_blank" rel="noopener">立即开始(免费体验)</a>
|
||||
<a class="btn btn-ghost btn-lg" href="/docs/quickstart" target="_blank" rel="noopener">快速开始 →</a>
|
||||
</div>
|
||||
<div class="hero-pills">
|
||||
<span>本地优先</span><span>自定义 WS 握手头</span><span>SSE 断点续传</span><span>OpenAPI 双向同步</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 产品界面示意(纯 CSS 绘制) -->
|
||||
<div class="mock" aria-hidden="true">
|
||||
<div class="mock-titlebar"><i></i><i></i><i></i><span>OpsxAPI · 示例集合</span></div>
|
||||
<div class="mock-body">
|
||||
<div class="mock-side">
|
||||
<div class="mock-search"></div>
|
||||
<div class="mock-group"><svg class="mi"><use href="#i-library"/></svg> 示例集合</div>
|
||||
<div class="mock-item"><b class="m-get">GET</b>获取请求回显</div>
|
||||
<div class="mock-item"><b class="m-post">POST</b>提交 JSON</div>
|
||||
<div class="mock-item"><b class="m-get">GET</b>Bearer 鉴权示例</div>
|
||||
<div class="mock-group"><svg class="mi"><use href="#i-radio"/></svg> 实时协议</div>
|
||||
<div class="mock-item active"><svg class="mi"><use href="#i-zap"/></svg> WebSocket · echo</div>
|
||||
<div class="mock-item"><svg class="mi"><use href="#i-mailbox"/></svg> MQTT · EMQX</div>
|
||||
<div class="mock-group"><svg class="mi"><use href="#i-hexagon"/></svg> GraphQL</div>
|
||||
<div class="mock-item">国家查询 · countries</div>
|
||||
</div>
|
||||
<div class="mock-main">
|
||||
<div class="mock-urlbar">
|
||||
<b class="m-post">POST</b>
|
||||
<span class="mock-url">{{baseUrl}}/v1/orders</span>
|
||||
<span class="mock-send">发送</span>
|
||||
</div>
|
||||
<div class="mock-req">
|
||||
<span class="tab on">Body</span><span class="tab">Params</span><span class="tab">Headers</span><span class="tab">Auth</span>
|
||||
<pre>{
|
||||
<i>"sku"</i>: <u>"A-1"</u>,
|
||||
<i>"qty"</i>: <u>2</u>
|
||||
}</pre>
|
||||
</div>
|
||||
<div class="mock-resp">
|
||||
<span class="tab on">响应体</span>
|
||||
<span class="mock-status">200 · 87ms · 274B</span>
|
||||
<pre>{
|
||||
<i>"orderId"</i>: <u>"ord_9f2a"</u>,
|
||||
<i>"status"</i>: <u>"created"</u>,
|
||||
<i>"paid"</i>: <u>false</u>
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== 为什么选 ===================== -->
|
||||
<section class="section">
|
||||
<div class="wrap">
|
||||
<h2 class="sec-title">为什么团队选择 OpsxAPI</h2>
|
||||
<p class="sec-sub">不追赶云端协作的花活,专注把「调试接口」这件事做到极致。</p>
|
||||
<div class="grid g4">
|
||||
<div class="card">
|
||||
<div class="card-icon"><svg class="i-lg"><use href="#i-shield-check"/></svg></div>
|
||||
<h3>本地优先,数据主权</h3>
|
||||
<p>集合、环境、历史全部保存在本机,不经过任何第三方服务器。内网、涉密项目放心用。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon"><svg class="i-lg"><use href="#i-arrow-left-right"/></svg></div>
|
||||
<h3>六协议一个窗口</h3>
|
||||
<p>HTTP / GraphQL / WebSocket / SSE / Socket.IO / MQTT 统一工作台,实时消息分色日志。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon"><svg class="i-lg"><use href="#i-plug-zap"/></svg></div>
|
||||
<h3>浏览器也能带握手头</h3>
|
||||
<p>内置 WebSocket 隧道与流式代理,突破浏览器 CORS 与 Header 限制,调试网关级接口。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon"><svg class="i-lg"><use href="#i-package"/></svg></div>
|
||||
<h3>生态互通</h3>
|
||||
<p>Apifox / Postman / Swagger / cURL / HAR 一键导入导出,迁移零成本,文档即代码。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== 功能 ===================== -->
|
||||
<section class="section alt" id="features">
|
||||
<div class="wrap">
|
||||
<h2 class="sec-title">开箱即用的专业能力</h2>
|
||||
<p class="sec-sub">从单人调试到团队交付,该有的都有。</p>
|
||||
<div class="grid g3">
|
||||
<div class="card row"><div class="card-icon"><svg class="i-lg"><use href="#i-folder-tree"/></svg></div><div><h3>集合与多标签</h3><p>目录树、搜索、右键菜单、拖拽移动;多标签并行调试。</p></div></div>
|
||||
<div class="card row"><div class="card-icon"><svg class="i-lg"><use href="#i-globe"/></svg></div><div><h3>多环境变量</h3><p><code>{{baseUrl}}</code> 全局替换,正式 / 测试 / 本地一键切换。</p></div></div>
|
||||
<div class="card row"><div class="card-icon"><svg class="i-lg"><use href="#i-shield-check"/></svg></div><div><h3>鉴权管理</h3><p>Bearer / Basic / API Key,token 走变量,一处配置全局生效。</p></div></div>
|
||||
<div class="card row"><div class="card-icon"><svg class="i-lg"><use href="#i-zap"/></svg></div><div><h3>实时协议调试</h3><p>WS 握手头、SSE 断点续传、Socket.IO 事件收发、MQTT 通配符订阅。</p></div></div>
|
||||
<div class="card row"><div class="card-icon"><svg class="i-lg"><use href="#i-code"/></svg></div><div><h3>代码生成</h3><p>任意请求一键生成 cURL / fetch / Python / Go,直接进脚本与 CI。</p></div></div>
|
||||
<div class="card row"><div class="card-icon"><svg class="i-lg"><use href="#i-book-open"/></svg></div><div><h3>内置文档站</h3><p>随服务自带 /docs 用户文档与 Markdown 接口文档导出,交付即完整。</p></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== 协议 ===================== -->
|
||||
<section class="section" id="protocols">
|
||||
<div class="wrap">
|
||||
<h2 class="sec-title">六种协议,全覆盖</h2>
|
||||
<p class="sec-sub">每个协议都有为它定制的编辑器,而不是塞进同一个表单。</p>
|
||||
<div class="grid g3">
|
||||
<div class="proto"><h3><svg class="i"><use href="#i-globe"/></svg> HTTP / REST</h3><p>7 种方法 · 6 种请求体 · 响应 Pretty 高亮 · 历史回跳</p></div>
|
||||
<div class="proto"><h3><svg class="i"><use href="#i-hexagon"/></svg> GraphQL</h3><p>Query + Variables 编辑 · errors 高亮 · 生成 cURL 报文</p></div>
|
||||
<div class="proto"><h3><svg class="i"><use href="#i-zap"/></svg> WebSocket</h3><p>自定义握手 Header(本地隧道)· 子协议 · 分色消息日志</p></div>
|
||||
<div class="proto"><h3><svg class="i"><use href="#i-radio"/></svg> SSE</h3><p>直连失败自动切代理 · Last-Event-ID 续传 · 事件按名称归类</p></div>
|
||||
<div class="proto"><h3><svg class="i"><use href="#i-plug-zap"/></svg> Socket.IO</h3><p>事件监听(onAny)· emit 参数 JSON · path / auth 配置</p></div>
|
||||
<div class="proto"><h3><svg class="i"><use href="#i-mailbox"/></svg> MQTT</h3><p>wss Broker · 通配符订阅 · QoS 0/1/2 · 断线自动重连</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== 导入导出 ===================== -->
|
||||
<section class="section alt" id="exchange">
|
||||
<div class="wrap">
|
||||
<h2 class="sec-title">导入导出,迁移零成本</h2>
|
||||
<p class="sec-sub">粘贴、文件、URL 三种来源,格式自动识别。</p>
|
||||
<div class="badges">
|
||||
<span>OpenAPI 3.0 / 3.1</span><span>Swagger 2.0</span><span>Apifox</span><span>Postman v2.1</span>
|
||||
<span>Apipost</span><span>cURL</span><span>HAR</span><span>Insomnia</span><span>Hoppscotch</span><span>Markdown</span>
|
||||
<span class="more">YApi / Apidoc / Apizza / Eolink ①</span>
|
||||
</div>
|
||||
<p class="footnote">① 经 OpenAPI / Postman 格式中转导入。导入自动:tag 建目录、路径参数转变量、schema 生成示例体、security 映射鉴权。</p>
|
||||
<div class="exchange-actions">
|
||||
<div class="chip"><svg class="i"><use href="#i-download"/></svg> 导入</div><div class="exchange-arrow">──→</div><div class="chip big">OpsxAPI</div><div class="exchange-arrow">──→</div>
|
||||
<div class="chip"><svg class="i"><use href="#i-upload"/></svg> OpenAPI / Postman</div><div class="chip"><svg class="i"><use href="#i-upload"/></svg> Markdown 文档</div><div class="chip"><svg class="i"><use href="#i-upload"/></svg> 代码生成</div><div class="chip"><svg class="i"><use href="#i-upload"/></svg> 全量备份</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== 对比 ===================== -->
|
||||
<section class="section" id="compare">
|
||||
<div class="wrap">
|
||||
<h2 class="sec-title">与主流工具的对比</h2>
|
||||
<p class="sec-sub">价格为各官网公开标价(2026-09 整理),以官方为准。</p>
|
||||
<div class="table-wrap">
|
||||
<table class="cmp">
|
||||
<thead><tr><th>能力</th><th class="hl">OpsxAPI</th><th>Apifox</th><th>Apipost</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>入门付费价格</td><td class="hl"><b>¥99/年</b>(个人)</td><td>¥99/人/年②</td><td>免费 + 商业版</td></tr>
|
||||
<tr><td>专业档价格</td><td class="hl"><b>¥99/人/年</b>(团队)</td><td>¥199/人/年</td><td>按席位订阅</td></tr>
|
||||
<tr><td>数据存储位置</td><td class="hl"><b>100% 本地</b></td><td>云端 / 私有化</td><td>云端 / 私有化</td></tr>
|
||||
<tr><td>调试协议</td><td class="hl">HTTP · GraphQL · WS · SSE · Socket.IO · MQTT</td><td>HTTP · WS · GraphQL</td><td>HTTP · WS · gRPC 等</td></tr>
|
||||
<tr><td>WS 自定义握手 Header(Web)</td><td class="hl"><b>✓ 内置隧道</b></td><td>桌面端支持</td><td>桌面端支持</td></tr>
|
||||
<tr><td>自托管部署</td><td class="hl"><b>单二进制 + 静态页</b></td><td>私有化付费</td><td>私有化付费</td></tr>
|
||||
<tr><td>私有化 / 买断参考价</td><td class="hl"><b>¥8,000/年起 · ¥30,000 买断</b></td><td>询价</td><td>询价</td></tr>
|
||||
<tr><td>内置用户文档站</td><td class="hl"><b>✓ /docs</b></td><td>—</td><td>—</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p class="footnote">② Apifox 免费版即含大部分调试能力,付费档面向团队协作;对比仅帮助按需选型,各有所长。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== 定价 ===================== -->
|
||||
<section class="section alt" id="pricing">
|
||||
<div class="wrap">
|
||||
<h2 class="sec-title">简单透明的定价</h2>
|
||||
<p class="sec-sub">先在工作台免费用起来,需要授权与服务时再付费。</p>
|
||||
<div class="pricing three">
|
||||
<div class="price-card">
|
||||
<div class="plan">个人版</div>
|
||||
<div class="price"><b>¥99</b><span>/ 年</span></div>
|
||||
<div class="plan-sub">单设备授权 · 全功能</div>
|
||||
<ul>
|
||||
<li>✓ 六种协议全部调试能力</li>
|
||||
<li>✓ 集合 / 环境 / 历史无限量</li>
|
||||
<li>✓ 全格式导入导出 + 代码生成</li>
|
||||
<li>✓ 桌面端 + Web 双形态</li>
|
||||
<li>✓ 1 年免费升级</li>
|
||||
</ul>
|
||||
<a class="btn btn-primary" href="#contact">购买咨询</a>
|
||||
</div>
|
||||
<div class="price-card featured">
|
||||
<div class="plan-tag">高性价比之选</div>
|
||||
<div class="plan">团队版</div>
|
||||
<div class="price"><b>¥99</b><span>/ 人 / 年</span></div>
|
||||
<div class="plan-sub">按席位授权 · 5 席起</div>
|
||||
<ul>
|
||||
<li>✓ 个人版全部能力</li>
|
||||
<li>✓ 集合批量导入分发</li>
|
||||
<li>✓ 内网自托管(单二进制)</li>
|
||||
<li>✓ 优先技术支持</li>
|
||||
<li>✓ 1 年免费升级</li>
|
||||
</ul>
|
||||
<a class="btn btn-primary" href="#contact">联系商务</a>
|
||||
</div>
|
||||
<div class="price-card enterprise">
|
||||
<div class="plan-tag dark">私有化 · 单法人不限席位</div>
|
||||
<div class="plan">企业私有化版</div>
|
||||
<div class="price"><b>¥8,000</b><span>/ 年 · 标价</span></div>
|
||||
<div class="plan-sub">或 ¥30,000 一次性永久买断(标价)</div>
|
||||
<ul>
|
||||
<li>✓ 整机 / 内网部署,数据不出企业</li>
|
||||
<li>✓ 不限席位,团队版全部能力</li>
|
||||
<li>✓ 远程部署协助 + 专属支持</li>
|
||||
<li>✓ 定制开发通道(另行报价)</li>
|
||||
<li>✓ 买断可选:永久使用 + 1 年升级</li>
|
||||
</ul>
|
||||
<a class="btn btn-dark" href="#contact">获取报价</a>
|
||||
<div class="deal-note">
|
||||
标价透明,成交灵活:经早鸟 / 教育 / 多年预付 / 案例合作,常见成交区间 <b>¥5,000–8,000/年</b>、买断 <b>¥15,000–30,000</b>。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 让利策略 -->
|
||||
<h3 class="growth-title">早鸟与让利计划 —— 把优惠变成共同成长</h3>
|
||||
<div class="grid g4">
|
||||
<div class="card mini">
|
||||
<h3><svg class="i-lg"><use href="#i-sprout"/></svg> 早鸟创始价</h3>
|
||||
<p>前 20 个付费席位享 <b>5-7 折永久优惠</b>,并进入创始客户名单,参与需求优先排期。</p>
|
||||
</div>
|
||||
<div class="card mini">
|
||||
<h3><svg class="i-lg"><use href="#i-graduation-cap"/></svg> 教育 / 开源</h3>
|
||||
<p>高校师生、开源维护者凭证享 <b>6-8 折</b>。教育用途可申请批量授权。</p>
|
||||
</div>
|
||||
<div class="card mini">
|
||||
<h3><svg class="i-lg"><use href="#i-credit-card"/></svg> 多年预付</h3>
|
||||
<p>一次预付 2 年 <b>9 折</b>、3 年 <b>8 折</b>,锁定当期价格,改善双方现金流。</p>
|
||||
</div>
|
||||
<div class="card mini">
|
||||
<h3><svg class="i-lg"><use href="#i-megaphone"/></svg> 案例换授权</h3>
|
||||
<p>愿意公开使用案例、撰写评测或作为推荐客户的,可获 <b>额外折扣或免费席位</b>。</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="footnote">以上为官网标价机制;最终成交价支持按规模与场景商务沟通,多年预付 / 服务范围(部署协助、定制开发)可组合。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== FAQ ===================== -->
|
||||
<section class="section" id="faq">
|
||||
<div class="wrap narrow">
|
||||
<h2 class="sec-title">购买常见问题</h2>
|
||||
<div class="faq">
|
||||
<details open><summary>授权如何交付?包含哪些内容?</summary><p>邮件交付授权码与部署包(单二进制 + Web 静态资源),含使用文档。团队版附内网部署指南。</p></details>
|
||||
<details><summary>一年后不续费还能用吗?</summary><p>可以。授权期内下载的版本可 <b>永久使用</b>,续费获得后续升级与支持。</p></details>
|
||||
<details><summary>团队版怎么计费?人员变动怎么办?</summary><p>按启用席位每年计费,5 席起。人员变动可在授权期内置换席位,无需额外费用。</p></details>
|
||||
<details><summary>可以开发票吗?</summary><p>支持增值税普通发票与专票,对公转账或线上支付均可。</p></details>
|
||||
<details><summary>我们能自己部署吗?安全吗?</summary><p>完全可以,这正是 OpsxAPI 的设计初衷:数据 100% 本地,服务仅监听本机回环,内网部署无外联。</p></details>
|
||||
<details><summary>可以永久买断吗?</summary><p>可以。企业私有化版支持一次性买断(标价 ¥30,000,含 1 年免费升级与支持),买断版本<b>永久使用</b>,续费仅用于继续获得升级。订阅档同理:授权期内下载的版本永久可用。</p></details>
|
||||
<details><summary>预算有限,有优惠空间吗?</summary><p>我们不直接降价,但提供四种「让利换增长」机制:<b>早鸟创始价</b>(前 20 席 5–7 折永久)、<b>教育 / 开源</b> 6–8 折、<b>多年预付</b> 8–9 折、<b>以案例 / 评测换折扣</b>。相比按年订阅的 SaaS,5 人以上团队通常 1–2 年即可回本;可自行部署(我们提供文档)或以案例分享换取创始价。</p></details>
|
||||
<details><summary>先试用再决定可以吗?</summary><p>欢迎。工作台全部调试能力可直接体验;授权版差异主要在商业支持与升级服务。</p></details>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== CTA / 联系 ===================== -->
|
||||
<section class="cta" id="contact">
|
||||
<div class="wrap">
|
||||
<h2>把接口调试收回自己手里</h2>
|
||||
<p>现在打开工作台,30 秒发出第一个请求;采购事宜邮件联系,当天回复。</p>
|
||||
<div class="hero-actions center">
|
||||
<a class="btn btn-inverse btn-lg" href="/" target="_blank" rel="noopener">打开工作台 ↗</a>
|
||||
<a class="btn btn-outline btn-lg" href="mailto:hello@opsxapi.dev?subject=OpsxAPI%20购买咨询">hello@opsxapi.dev</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== Footer ===================== -->
|
||||
<footer class="footer">
|
||||
<div class="wrap footer-grid">
|
||||
<div>
|
||||
<div class="brand"><span class="brand-logo"><svg width="30" height="30" viewBox="0 0 64 64" aria-label="OpsxAPI"><defs><linearGradient id="lg-site" x1="0" y1="0" x2="64" y2="64" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2563EB"/><stop offset="1" stop-color="#06B6D4"/></linearGradient></defs><rect width="64" height="64" rx="14" fill="url(#lg-site)"/><g stroke="#fff" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" fill="none"><path d="M17 25h26"/><path d="M37 19l6 6-6 6"/><path d="M47 39H21"/><path d="M27 33l-6 6 6 6"/></g></svg></span><span class="brand-name">OpsxAPI</span></div>
|
||||
<p class="footer-desc">本地优先的全协议接口工作台。<br>数据在自己手里,才是真的效率工具。</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4>产品</h4>
|
||||
<a href="#features">功能特性</a>
|
||||
<a href="#pricing">定价与让利</a>
|
||||
<a href="#compare">工具对比</a>
|
||||
</div>
|
||||
<div>
|
||||
<h4>资源</h4>
|
||||
<a href="/docs" target="_blank" rel="noopener">用户文档</a>
|
||||
<a href="/docs/server-api" target="_blank" rel="noopener">服务端 API</a>
|
||||
<a href="/docs/faq" target="_blank" rel="noopener">常见问题</a>
|
||||
</div>
|
||||
<div>
|
||||
<h4>支持</h4>
|
||||
<a href="mailto:hello@opsxapi.dev">hello@opsxapi.dev</a>
|
||||
<a href="/docs/quickstart" target="_blank" rel="noopener">快速开始</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bar">© 2026 OpsxAPI · 本页由单二进制内置提供(/home)</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user