refactor: move project under backend
这个提交包含在:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,3 +1,2 @@
|
|||||||
app/node_modules/
|
# Local artifacts created before moving into backend/
|
||||||
app/data/
|
app/
|
||||||
*.log
|
|
||||||
|
|||||||
3
backend/.gitignore
vendored
普通文件
3
backend/.gitignore
vendored
普通文件
@@ -0,0 +1,3 @@
|
|||||||
|
app/node_modules/
|
||||||
|
app/data/
|
||||||
|
*.log
|
||||||
@@ -24,6 +24,7 @@ npm run dev
|
|||||||
```
|
```
|
||||||
|
|
||||||
服务默认监听 `:3001`(可在 `app/.env` 修改)。
|
服务默认监听 `:3001`(可在 `app/.env` 修改)。
|
||||||
|
通过域名访问时统一前缀为:`https://capay.hao.work/backend`。
|
||||||
|
|
||||||
### 免费套餐低频监控
|
### 免费套餐低频监控
|
||||||
在 `app/.env` 中已设置:
|
在 `app/.env` 中已设置:
|
||||||
@@ -35,6 +36,7 @@ npm run dev
|
|||||||
支持多个 Alchemy 免费 key:使用 `ALCHEMY_API_KEYS=key1,key2`,系统会按轮询周期进行轮转调用,降低单 key 的频率。
|
支持多个 Alchemy 免费 key:使用 `ALCHEMY_API_KEYS=key1,key2`,系统会按轮询周期进行轮转调用,降低单 key 的频率。
|
||||||
|
|
||||||
## API 说明(简化版)
|
## API 说明(简化版)
|
||||||
|
域名访问时请在路径前加 `/backend` 前缀(例如:`/backend/payments/orders`)。
|
||||||
### 1) 开通商户
|
### 1) 开通商户
|
||||||
`POST /merchants`
|
`POST /merchants`
|
||||||
```json
|
```json
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
APP_PORT=3001
|
APP_PORT=3001
|
||||||
APP_HOST=https://capay.hao.work
|
APP_HOST=https://capay.hao.work/backend
|
||||||
PLAN=free
|
PLAN=free
|
||||||
ADMIN_API_KEY=whoami139
|
ADMIN_API_KEY=whoami139
|
||||||
|
|
||||||
@@ -21,7 +21,9 @@ const curlTrackTx = document.getElementById('curl-track-tx');
|
|||||||
const webhookTip = document.getElementById('webhook-tip');
|
const webhookTip = document.getElementById('webhook-tip');
|
||||||
|
|
||||||
const STORAGE_KEY = 'capay_admin_key';
|
const STORAGE_KEY = 'capay_admin_key';
|
||||||
const baseUrl = window.location.origin;
|
const basePath = window.location.pathname.replace(/\/[^/]*$/, '/').replace(/\/+$/, '');
|
||||||
|
const apiBase = basePath || '';
|
||||||
|
const baseUrl = `${window.location.origin}${apiBase}`;
|
||||||
|
|
||||||
function setStatus(text, ok) {
|
function setStatus(text, ok) {
|
||||||
statusText.textContent = text;
|
statusText.textContent = text;
|
||||||
@@ -43,13 +45,18 @@ function setAdminKey(value) {
|
|||||||
localStorage.setItem(STORAGE_KEY, value);
|
localStorage.setItem(STORAGE_KEY, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function withBase(path) {
|
||||||
|
if (!apiBase) return path;
|
||||||
|
return `${apiBase}${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
async function request(path, options = {}) {
|
async function request(path, options = {}) {
|
||||||
const headers = options.headers ? { ...options.headers } : {};
|
const headers = options.headers ? { ...options.headers } : {};
|
||||||
const adminKey = getAdminKey();
|
const adminKey = getAdminKey();
|
||||||
if (adminKey) {
|
if (adminKey) {
|
||||||
headers['x-admin-key'] = adminKey;
|
headers['x-admin-key'] = adminKey;
|
||||||
}
|
}
|
||||||
const res = await fetch(path, { ...options, headers });
|
const res = await fetch(withBase(path), { ...options, headers });
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
let message = text;
|
let message = text;
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
<title>Capay API Docs</title>
|
<title>Capay API Docs</title>
|
||||||
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
|
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
|
||||||
<link rel="stylesheet" href="/styles.css" />
|
<link rel="stylesheet" href="../styles.css" />
|
||||||
<style>
|
<style>
|
||||||
.docs-header {
|
.docs-header {
|
||||||
max-width: 1180px;
|
max-width: 1180px;
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<h2>Capay API Docs</h2>
|
<h2>Capay API Docs</h2>
|
||||||
<p class="muted">Swagger-style documentation with live requests.</p>
|
<p class="muted">Swagger-style documentation with live requests.</p>
|
||||||
</div>
|
</div>
|
||||||
<a class="pill link" href="/">Back to Admin</a>
|
<a class="pill link" href="../">Back to Admin</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="swagger-ui"></div>
|
<div id="swagger-ui"></div>
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
<script>
|
<script>
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
window.ui = SwaggerUIBundle({
|
window.ui = SwaggerUIBundle({
|
||||||
url: "/openapi.json",
|
url: "../openapi.json",
|
||||||
dom_id: "#swagger-ui",
|
dom_id: "#swagger-ui",
|
||||||
deepLinking: true,
|
deepLinking: true,
|
||||||
presets: [SwaggerUIBundle.presets.apis],
|
presets: [SwaggerUIBundle.presets.apis],
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
<title>Capay 管理后台</title>
|
<title>Capay 管理后台</title>
|
||||||
<link rel="stylesheet" href="/styles.css" />
|
<link rel="stylesheet" href="styles.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<div class="hero-meta">
|
<div class="hero-meta">
|
||||||
<span class="pill">API Admin</span>
|
<span class="pill">API Admin</span>
|
||||||
<span class="pill" id="status-pill">未连接</span>
|
<span class="pill" id="status-pill">未连接</span>
|
||||||
<a class="pill link" href="/docs/">API Docs</a>
|
<a class="pill link" href="docs/">API Docs</a>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -131,6 +131,6 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/app.js"></script>
|
<script src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"url": "/"
|
"url": "/backend"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"HOST": "https://capay.hao.work",
|
"HOST": "https://capay.hao.work/backend",
|
||||||
"PORT": 8081,
|
"PORT": 8081,
|
||||||
"MONGO_CONNECTION_STRING": "mongodb://localhost:27017",
|
"MONGO_CONNECTION_STRING": "mongodb://localhost:27017",
|
||||||
"MONGO_KEAGATE_DB": "keagate",
|
"MONGO_KEAGATE_DB": "keagate",
|
||||||
@@ -13,8 +13,16 @@ server {
|
|||||||
# include /etc/letsencrypt/options-ssl-nginx.conf;
|
# include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||||
# ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
# ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||||
|
|
||||||
location / {
|
location = / {
|
||||||
proxy_pass http://127.0.0.1:3001;
|
return 302 /backend/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /backend {
|
||||||
|
return 301 /backend/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /backend/ {
|
||||||
|
proxy_pass http://127.0.0.1:3001/;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
在新工单中引用
屏蔽一个用户