镜像自地址
https://github.com/JGZYES/ParlzPackageManger.git
已同步 2026-09-11 05:42:44 +08:00
web: split into home + 4 pages (features/install/download/servers-status); detailed multi-endpoint status API (server header/latency/timestamp); shared _common.php layout
这个提交包含在:
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/* Shared layout + constants for the PMM site (4 pages + home).
|
||||
* Pure grayscale dark theme. Each page: define('PMM_SITE',1); require _common.php;
|
||||
* then pmm_header($active,$title); ... pmm_footer(); */
|
||||
if (!defined('PMM_SITE')) { http_response_code(403); exit('forbidden'); }
|
||||
|
||||
define('PMM_VERSION', '0.2.9');
|
||||
|
||||
function pmm_nav_links(string $active): string {
|
||||
$items = [
|
||||
'home' => ['index.php', '首页'],
|
||||
'features' => ['features.php', '特性'],
|
||||
'install' => ['install.php', '安装'],
|
||||
'download' => ['download.php', '下载'],
|
||||
'status' => ['servers.php', '服务状态'],
|
||||
];
|
||||
$html = '';
|
||||
foreach ($items as $key => [$href, $label]) {
|
||||
$cls = $key === $active ? ' class="active"' : '';
|
||||
$html .= '<li><a' . $cls . ' href="' . $href . '">' . $label . '</a></li>';
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
function pmm_header(string $active, string $title): void {
|
||||
$t = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
|
||||
echo '<!DOCTYPE html>' . "\n" .
|
||||
'<html lang="zh-CN">' . "\n" .
|
||||
'<head>' . "\n" .
|
||||
'<meta charset="UTF-8">' . "\n" .
|
||||
'<meta name="viewport" content="width=device-width, initial-scale=1.0">' . "\n" .
|
||||
'<title>' . $t . ' · ParlzPackageManager</title>' . "\n" .
|
||||
'<meta name="description" content="ParlzPackageManager (PMM) — 用 C 语言编写、跨 Windows/Linux/macOS 的包管理器。">' . "\n" .
|
||||
'<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=\'%23050505\'/%3E%3Ctext x=\'16\' y=\'23\' font-size=\'17\' font-family=\'monospace\' fill=\'%23cfcfcf\' text-anchor=\'middle\'%3Epmm%3C/text%3E%3C/svg%3E">' . "\n" .
|
||||
'<link rel="stylesheet" href="assets/style.css">' . "\n" .
|
||||
'</head>' . "\n" .
|
||||
'<body>' . "\n" .
|
||||
'<nav class="nav">' . "\n" .
|
||||
' <a class="brand" href="index.php"><span class="brand-mark">pmm</span><span class="brand-name">ParlzPackageManager</span></a>' . "\n" .
|
||||
' <ul class="nav-links">' . "\n" .
|
||||
' ' . pmm_nav_links($active) . "\n" .
|
||||
' <li><a class="btn btn-sm" href="https://github.com/JGZYES/ParlzPackageManger" target="_blank" rel="noopener">GitHub ↗</a></li>' . "\n" .
|
||||
' </ul>' . "\n" .
|
||||
'</nav>' . "\n";
|
||||
}
|
||||
|
||||
function pmm_page_open(string $kicker, string $title): void {
|
||||
$k = htmlspecialchars($kicker, ENT_QUOTES, 'UTF-8');
|
||||
$t = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
|
||||
echo '<header class="page-hero"><div class="wrap">' . "\n" .
|
||||
' <div class="page-kicker">' . $k . '</div>' . "\n" .
|
||||
' <h1>' . $t . '</h1>' . "\n" .
|
||||
'</div></header>' . "\n";
|
||||
}
|
||||
|
||||
function pmm_footer(): void {
|
||||
echo '<footer class="footer"><div class="wrap">' . "\n" .
|
||||
' <div class="footer-top"><span class="brand-name">ParlzPackageManager</span><span>GNU GPL-3.0 · C11 · 跨平台</span></div>' . "\n" .
|
||||
' <div class="footer-links">' . "\n" .
|
||||
' <a href="https://github.com/JGZYES/ParlzPackageManger" target="_blank" rel="noopener">GitHub</a>' . "\n" .
|
||||
' <a href="https://github.com/JGZYES/ParlzPackageManger/releases" target="_blank" rel="noopener">Releases</a>' . "\n" .
|
||||
' <a href="index.php">首页</a>' . "\n" .
|
||||
' </div>' . "\n" .
|
||||
' <p class="footer-note">© 2026 ParlzPackageManager Project · 深圳 sz.pmm.parlz.com · 香港 pmm.parlz.com</p>' . "\n" .
|
||||
'</div></footer>' . "\n" .
|
||||
'<script src="assets/main.js"></script>' . "\n" .
|
||||
'</body>' . "\n" . '</html>';
|
||||
}
|
||||
+51
-33
@@ -1,4 +1,4 @@
|
||||
/* PMM site — rotating dotted Earth globe + live status + tabs + copy */
|
||||
/* PMM site — rotating dotted Earth globe + detailed live status + tabs + copy */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
@@ -48,14 +48,9 @@
|
||||
var ctx = cv.getContext("2d");
|
||||
var R = window.PMM_DIMS.rows, C = window.PMM_DIMS.cols;
|
||||
var DG = Math.PI / 180;
|
||||
// map cell -> lat/lon
|
||||
var pts = window.PMM_MAP.map(function (p) {
|
||||
return {
|
||||
lat: (76 - p[0] * (152 / R)) * DG,
|
||||
lon: (-180 + p[1] * (360 / C)) * DG
|
||||
};
|
||||
return { lat: (76 - p[0] * (152 / R)) * DG, lon: (-180 + p[1] * (360 / C)) * DG };
|
||||
});
|
||||
// server locations (Shenzhen ~22.5N/114E, HK ~22.3N/114.2E)
|
||||
var servers = [
|
||||
{ lat: 22.54 * DG, lon: 114.06 * DG },
|
||||
{ lat: 22.32 * DG, lon: 114.17 * DG }
|
||||
@@ -74,14 +69,12 @@
|
||||
window.addEventListener("resize", resize);
|
||||
|
||||
var theta = 0, rafId = null;
|
||||
var ACC = "#d8d8d8", OKC = "#ffffff"; /* grayscale palette */
|
||||
|
||||
function draw(t) {
|
||||
theta += 0.0035;
|
||||
var cos = Math.cos, sin = Math.sin;
|
||||
ctx.clearRect(0, 0, W, H);
|
||||
|
||||
// sphere disc
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = "rgba(255,255,255,0.045)";
|
||||
@@ -90,19 +83,14 @@
|
||||
ctx.lineWidth = 1;
|
||||
ctx.stroke();
|
||||
|
||||
// faint equator + meridian rings for a globe feel
|
||||
ctx.strokeStyle = "rgba(255,255,255,0.11)";
|
||||
ctx.beginPath(); ctx.ellipse(cx, cy, radius, radius * 0.98, 0, 0, 2 * Math.PI); ctx.stroke();
|
||||
|
||||
function proj(lat, lon) {
|
||||
var r = lon + theta;
|
||||
var x = cos(lat) * sin(r);
|
||||
var y = sin(lat);
|
||||
var z = cos(lat) * cos(r);
|
||||
return { x: x, y: y, z: z };
|
||||
return { x: cos(lat) * sin(r), y: sin(lat), z: cos(lat) * cos(r) };
|
||||
}
|
||||
|
||||
// land dot cloud — back pass (z<0) dim, front pass (z>0) bright
|
||||
for (var pass = 0; pass < 2; pass++) {
|
||||
var front = pass === 1;
|
||||
for (var i = 0; i < pts.length; i++) {
|
||||
@@ -113,7 +101,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// server location markers (pulse) — only if on the visible hemisphere
|
||||
servers.forEach(function (s) {
|
||||
var q = proj(s.lat, s.lon);
|
||||
if (q.z <= 0) return;
|
||||
@@ -121,7 +108,7 @@
|
||||
var pulse = 0.6 + 0.4 * sin(t * 0.006);
|
||||
ctx.beginPath();
|
||||
ctx.arc(sx, sy, 3, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = OKC;
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(sx, sy, 4 + (1 - pulse) * 4, 0, 2 * Math.PI);
|
||||
@@ -133,7 +120,6 @@
|
||||
rafId = requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
// only animate while visible
|
||||
if ("IntersectionObserver" in window) {
|
||||
var io = new IntersectionObserver(function (en) {
|
||||
en.forEach(function (e) {
|
||||
@@ -147,24 +133,56 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Live mirror status ---------- */
|
||||
/* ---------- Detailed live mirror status (servers.php) ---------- */
|
||||
function esc(s) {
|
||||
return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) {
|
||||
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c];
|
||||
});
|
||||
}
|
||||
function ms(v) { return (v == null ? "—" : v + " ms"); }
|
||||
|
||||
function renderStatus(data) {
|
||||
if (!Array.isArray(data)) return;
|
||||
var detail = document.getElementById("svc-detail");
|
||||
var html = "";
|
||||
data.forEach(function (s, i) {
|
||||
var dot = document.getElementById("dot-" + i);
|
||||
var meta = document.getElementById("meta-" + i);
|
||||
if (dot) dot.className = "status-dot " + (s.online ? "up" : "down");
|
||||
if (meta)
|
||||
meta.innerHTML = s.online
|
||||
? "在线 · 平均 <b>" + esc(s.avg_ms) + "</b> ms"
|
||||
: "离线 / 不可达";
|
||||
html += '<div class="svc-card">'
|
||||
+ '<h3><span class="status-dot ' + (s.online ? "up" : "down") + '"></span>'
|
||||
+ esc(s.name) + ' · <span style="color:#9a9a9a;font-weight:400">' + esc(s.region) + '</span></h3>'
|
||||
+ '<div class="svc-host">' + esc(s.host) + ' · 检测时间 ' + esc(s.checked_at) + '</div>'
|
||||
+ '<table class="svc-table"><tr><th>检测项</th><th>状态</th><th>HTTP</th><th>延迟</th><th>Server</th></tr>';
|
||||
(s.checks || []).forEach(function (c) {
|
||||
var r = c.result || {};
|
||||
html += '<tr><td>' + esc(c.item) + '<br><code>' + esc(c.url) + '</code></td>'
|
||||
+ '<td><span class="svc-badge ' + (r.ok ? "up" : "down") + '">' + (r.ok ? "正常" : "异常") + '</span></td>'
|
||||
+ '<td class="' + (r.ok ? "svc-ok" : "svc-bad") + '">' + (r.code || "—") + '</td>'
|
||||
+ '<td>' + ms(r.ms) + '</td>'
|
||||
+ '<td><code>' + esc(r.server || "—") + '</code></td></tr>';
|
||||
});
|
||||
html += '</table></div>';
|
||||
});
|
||||
if (detail) detail.innerHTML = html;
|
||||
var upd = document.getElementById("status-updated");
|
||||
if (upd && data[0] && data[0].checked_at) upd.textContent = "最近检测:" + data[0].checked_at + "(UTC)";
|
||||
}
|
||||
|
||||
function loadStatus() {
|
||||
fetch("status.php", { cache: "no-store" })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (!Array.isArray(data)) return;
|
||||
data.forEach(function (s, i) {
|
||||
var dot = document.getElementById("dot-" + i);
|
||||
var meta = document.getElementById("meta-" + i);
|
||||
if (!dot || !meta) return;
|
||||
dot.className = "status-dot " + (s.online ? "up" : "down");
|
||||
meta.innerHTML = s.online
|
||||
? "在线 · <b>" + s.ms + "</b> ms · HTTP " + s.code
|
||||
: "离线 / 不可达";
|
||||
});
|
||||
})
|
||||
.then(renderStatus)
|
||||
.catch(function () {});
|
||||
}
|
||||
loadStatus();
|
||||
setInterval(loadStatus, 12000);
|
||||
var refreshBtn = document.getElementById("status-refresh");
|
||||
if (refreshBtn) refreshBtn.addEventListener("click", loadStatus);
|
||||
if (document.getElementById("svc-detail")) {
|
||||
loadStatus();
|
||||
setInterval(loadStatus, 15000);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -136,3 +136,40 @@ code{font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,"Co
|
||||
.hero-globe{max-width:280px}
|
||||
.nav-links{display:none}
|
||||
}
|
||||
|
||||
/* ---------- multi-page layout ---------- */
|
||||
.nav-links a.active{color:#fff;font-weight:700}
|
||||
.page-hero{border-bottom:1px solid var(--line);background:var(--bg2)}
|
||||
.page-hero .wrap{padding:46px 22px 34px}
|
||||
.page-kicker{font-size:12px;letter-spacing:4px;color:var(--dim);text-transform:uppercase;margin-bottom:10px}
|
||||
.page-hero h1{font-size:clamp(28px,4vw,40px);font-weight:800;letter-spacing:-.5px}
|
||||
|
||||
/* home entry grid */
|
||||
.entry-section{padding-top:34px}
|
||||
.entry-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:16px}
|
||||
.entry-card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:24px;
|
||||
display:flex;flex-direction:column;gap:8px;transition:transform .2s,border-color .2s}
|
||||
.entry-card:hover{transform:translateY(-3px);border-color:#fff}
|
||||
.entry-icon{font-size:22px;color:#fff}
|
||||
.entry-title{font-size:17px;font-weight:800}
|
||||
.entry-desc{font-size:13.5px;color:var(--muted)}
|
||||
|
||||
/* status page */
|
||||
.status-toolbar{display:flex;align-items:center;justify-content:space-between;gap:14px;
|
||||
max-width:760px;margin:0 auto 18px;flex-wrap:wrap}
|
||||
.status-updated{font-size:13px;color:var(--dim);font-family:ui-monospace,monospace}
|
||||
.svc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:18px;max-width:760px;margin:0 auto}
|
||||
.svc-detail-wrap{max-width:760px;margin:34px auto 0;display:grid;gap:22px}
|
||||
.svc-card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:22px}
|
||||
.svc-card h3{display:flex;align-items:center;gap:10px;font-size:16px;margin-bottom:4px}
|
||||
.svc-host{font-family:ui-monospace,monospace;font-size:12.5px;color:var(--dim);margin-bottom:14px}
|
||||
.svc-table{width:100%;border-collapse:collapse;font-size:13.5px}
|
||||
.svc-table th{ text-align:left;color:var(--dim);font-weight:600;padding:8px 10px;border-bottom:1px solid var(--line)}
|
||||
.svc-table td{padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:top;color:var(--muted)}
|
||||
.svc-table tr:last-child td{border-bottom:0}
|
||||
.svc-table code{font-size:12px;color:#bdbdbd;word-break:break-all}
|
||||
.svc-badge{display:inline-block;font-size:11.5px;border-radius:999px;padding:2px 10px;border:1px solid var(--line);color:var(--muted);white-space:nowrap}
|
||||
.svc-badge.up{color:#fff;border-color:#fff}
|
||||
.svc-badge.down{color:#bbb;border-style:dashed}
|
||||
.svc-ok{color:#fff;font-weight:700}
|
||||
.svc-bad{color:#9a9a9a}
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
pmm_header('download', '下载');
|
||||
pmm_page_open('DOWNLOAD', '下载 PMM');
|
||||
?>
|
||||
|
||||
<section class="section">
|
||||
<div class="wrap">
|
||||
<p class="section-sub">可从官方镜像站或 GitHub Release 获取。镜像地址不变。</p>
|
||||
<div class="dl-grid">
|
||||
<a class="dl-card" href="downloads/pmm-<?php echo PMM_VERSION; ?>-windows-amd64.zip" download>
|
||||
<span class="dl-os">Windows</span><span class="dl-file">pmm-<?php echo PMM_VERSION; ?>-windows-amd64.zip · 免安装</span><span class="dl-arrow">↓</span>
|
||||
</a>
|
||||
<a class="dl-card" href="downloads/pmm_<?php echo PMM_VERSION; ?>_amd64.deb" download>
|
||||
<span class="dl-os">Linux</span><span class="dl-file">pmm_<?php echo PMM_VERSION; ?>_amd64.deb</span><span class="dl-arrow">↓</span>
|
||||
</a>
|
||||
<a class="dl-card" href="install.sh" download>
|
||||
<span class="dl-os">Linux/macOS</span><span class="dl-file">install.sh · 一键安装</span><span class="dl-arrow">↓</span>
|
||||
</a>
|
||||
<a class="dl-card" href="https://github.com/JGZYES/ParlzPackageManger/releases/latest" target="_blank" rel="noopener">
|
||||
<span class="dl-os">全部</span><span class="dl-file">GitHub Releases / 源码</span><span class="dl-arrow">↗</span>
|
||||
</a>
|
||||
</div>
|
||||
<p class="tips">Linux 也可 <code>curl -sSL https://pmm.parlz.com/install.sh | bash</code>;<code>.pdm</code> 包可直接 <code>pmm install pmm</code> 安装。</p>
|
||||
|
||||
<h2 class="section-title" style="margin-top:56px">镜像 <span class="accent">源</span></h2>
|
||||
<p class="section-sub">PMM 默认镜像(apt 式,按优先级回退),地址与 <code>mirror.ini</code> 一致。</p>
|
||||
<div class="mirror-list">
|
||||
<div class="mirror-item"><span class="mirror-tag default">sz · 默认</span><code>https://sz.pmm.parlz.com/mirror/packages</code></div>
|
||||
<div class="mirror-item"><span class="mirror-tag">main · 备份</span><code>https://pmm.parlz.com/mirror/packages</code></div>
|
||||
</div>
|
||||
<div style="text-align:center"><a class="btn btn-ghost" href="mirror/" target="_blank" rel="noopener">浏览镜像目录 ↗</a></div>
|
||||
<p class="tips">全部历史版本的 <code>.pdm</code> 都在镜像 <code>mirror/packages/pmm/</code> 下。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php pmm_footer(); ?>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
pmm_header('features', '特性');
|
||||
pmm_page_open('FEATURES', '核心特性');
|
||||
?>
|
||||
|
||||
<section class="section">
|
||||
<div class="wrap">
|
||||
<div class="grid">
|
||||
<div class="card"><h3>apt 式多镜像源</h3><p>支持 <code>mirror.ini</code> / <code>mirror.conf</code>,按优先级自动回退;默认自带 <code>sz</code>(深圳)与 <code>main</code>(香港)两个镜像,缺失时自动生成。</p></div>
|
||||
<div class="card"><h3>Git Release 安装</h3><p><code>pmm install --git repo</code> 自动识别 GitHub / GitLab / Gitea / Forgejo 及私有部署;支持 <code>repo@tag</code> / <code>-b branch</code> 安装指定版本。</p></div>
|
||||
<div class="card"><h3>.pdm 打包格式</h3><p><code>pmm pack <dir></code> 生成含 <code>control.tar.gz</code> / <code>data.tar.gz</code> / <code>sha256sums</code> 的归档,思路同 .deb。</p></div>
|
||||
<div class="card"><h3>依赖解析</h3><p><code>pdm-control</code> 声明 <code>Depends:</code>,registry 包声明 <code>"depends": [...]</code>;安装时自动递归解析并按版本约束(==/≥/≤)安装,带防环保护。</p></div>
|
||||
<div class="card"><h3>并发下载 + 断点续传</h3><p>大文件(≥8MB)自动 4 路 Range 分片并行下载、逐片续传、拼接后校验;失败自动回退单流。进度条含已用/剩余时间与实时速度。</p></div>
|
||||
<div class="card"><h3>哈希校验</h3><p>内置 SHA-256 与 SHA-1(FIPS 180-4),下载后强校验;<code>pmm verify <file></code> 随时验证任意安装包。</p></div>
|
||||
<div class="card"><h3>本地包安装</h3><p><code>pmm install -dpkg x.deb</code>(Linux)、<code>pmm install -msi x.msi</code>(Windows),也支持 <code>.rpm/.zip/.tar.gz/.exe/.dmg</code> 等,直接装文件或 URL。</p></div>
|
||||
<div class="card"><h3>搜索与信息</h3><p><code>pmm search <关键词></code> 发现镜像里的包;<code>pmm info <pkg></code> 查看版本与平台变体;<code>pmm list</code> 列出已装包。</p></div>
|
||||
<div class="card"><h3>缓存管理</h3><p>下载缓存统一放在 <code><base>/cache</code>;<code>pmm cache clean</code> 一键清空,<code>--no-cache</code> 强制重新下载。</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php pmm_footer(); ?>
|
||||
+30
-151
@@ -1,13 +1,9 @@
|
||||
<?php
|
||||
/*
|
||||
* ParlzPackageManager (PMM) — official site (dark, pure flat colors).
|
||||
* Hero: left = PMM intro, right = rotating dotted Earth globe (canvas).
|
||||
* A status panel monitors the SZ + HK mirror servers via status.php.
|
||||
* Static PHP, no dependencies.
|
||||
*/
|
||||
$VERSION = '0.2.9';
|
||||
/* PMM home — hero + rotating dotted Earth + entry cards to the 4 pages. */
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
|
||||
/* Coarse world map, '#'=land, ' '=ocean. Reused as the dot cloud on the globe. */
|
||||
/* Coarse world map, '#'=land — reused as the dot cloud on the globe. */
|
||||
$map = [
|
||||
" ",
|
||||
" #################### ",
|
||||
@@ -45,56 +41,29 @@ $map = [
|
||||
" ",
|
||||
];
|
||||
$ROWS = count($map);
|
||||
/* Normalize every row to the same width so $map[$r][$c] never overflows
|
||||
* (a shorter row otherwise triggers "Uninitialized string offset" warnings). */
|
||||
$COLS = 0;
|
||||
foreach ($map as $row) $COLS = max($COLS, strlen($row));
|
||||
for ($r = 0; $r < $ROWS; $r++) $map[$r] = str_pad($map[$r], $COLS);
|
||||
if ($COLS === 0) $COLS = 1;
|
||||
|
||||
/* Land cell coordinates for the globe dot cloud */
|
||||
$dots = array();
|
||||
for ($r = 0; $r < $ROWS; $r++)
|
||||
for ($c = 0; $c < $COLS; $c++)
|
||||
if ($map[$r][$c] === '#') $dots[] = array($r, $c);
|
||||
|
||||
pmm_header('home', '跨平台 C 语言包管理器');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ParlzPackageManager · 跨平台 C 语言包管理器</title>
|
||||
<meta name="description" content="ParlzPackageManager (PMM) — 用 C 语言编写、跨 Windows/Linux/macOS 的包管理器,apt 式多镜像源、git release 安装、.pdm 打包格式、SHA-256 校验。">
|
||||
<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='%23050505'/%3E%3Ctext x='16' y='23' font-size='17' font-family='monospace' fill='%23cfcfcf' text-anchor='middle'%3Epmm%3C/text%3E%3C/svg%3E">
|
||||
<link rel="stylesheet" href="assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="nav">
|
||||
<a class="brand" href="#top"><span class="brand-mark">pmm</span><span class="brand-name">ParlzPackageManager</span></a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="#features">特性</a></li>
|
||||
<li><a href="#install">安装</a></li>
|
||||
<li><a href="#download">下载</a></li>
|
||||
<li><a href="#status">服务状态</a></li>
|
||||
<li><a class="btn btn-sm" href="https://github.com/JGZYES/ParlzPackageManger" target="_blank" rel="noopener">GitHub ↗</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- Two-column hero: left intro / right dotted Earth globe -->
|
||||
<header id="top" class="hero">
|
||||
<div class="hero-inner">
|
||||
<div class="hero-copy">
|
||||
<div class="hero-badge">v<?php echo $VERSION; ?> · C11 · Windows / Linux / macOS</div>
|
||||
<div class="hero-badge">v<?php echo PMM_VERSION; ?> · C11 · Windows / Linux / macOS</div>
|
||||
<h1>ParlzPackageManager <span class="accent">(PMM)</span></h1>
|
||||
<p class="hero-tag">一个用 <strong>C 语言</strong>从零编写的跨平台包管理器。像 apt 一样多用镜像源,也能直接从 <strong>GitHub / GitLab / Gitea / Forgejo</strong> Release 装包。</p>
|
||||
<div class="hero-cta">
|
||||
<a class="btn btn-primary" href="#install">快速开始</a>
|
||||
<a class="btn btn-ghost" href="https://github.com/JGZYES/ParlzPackageManger/releases/latest" target="_blank" rel="noopener">最新版 ↓</a>
|
||||
</div>
|
||||
<div class="hero-note">
|
||||
<span class="dot ok"></span> 双镜像 · 深圳 / 香港 实时可用
|
||||
<a class="btn btn-primary" href="install.php">快速安装</a>
|
||||
<a class="btn btn-ghost" href="download.php">下载 v<?php echo PMM_VERSION; ?> ↓</a>
|
||||
</div>
|
||||
<div class="hero-note"><span class="dot ok"></span> 双镜像 · 深圳 / 香港 实时在线</div>
|
||||
</div>
|
||||
<div class="hero-globe">
|
||||
<canvas id="globe" aria-label="PMM 服务分布地球"></canvas>
|
||||
@@ -103,127 +72,37 @@ for ($r = 0; $r < $ROWS; $r++)
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section id="status" class="section">
|
||||
<section class="section entry-section">
|
||||
<div class="wrap">
|
||||
<h2 class="section-title">服务 <span class="accent">状态</span></h2>
|
||||
<p class="section-sub">实时监控 PMM 镜像服务器可用性与延迟(每 12 秒刷新)。</p>
|
||||
<div class="status-grid" id="status-grid">
|
||||
<div class="status-card">
|
||||
<div class="status-head"><span class="pin">深圳</span><span class="status-dot" id="dot-0"></span></div>
|
||||
<div class="status-ip">sz.pmm.parlz.com</div>
|
||||
<div class="status-host">https://sz.pmm.parlz.com/mirror/packages</div>
|
||||
<div class="status-meta" id="meta-0">检测中…</div>
|
||||
</div>
|
||||
<div class="status-card">
|
||||
<div class="status-head"><span class="pin">香港</span><span class="status-dot" id="dot-1"></span></div>
|
||||
<div class="status-ip">pmm.parlz.com</div>
|
||||
<div class="status-host">https://pmm.parlz.com/mirror/packages</div>
|
||||
<div class="status-meta" id="meta-1">检测中…</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="features" class="section alt">
|
||||
<div class="wrap">
|
||||
<h2 class="section-title">核心 <span class="accent">特性</span></h2>
|
||||
<div class="grid">
|
||||
<div class="card"><h3>apt 式多镜像源</h3><p>支持 <code>mirror.ini</code> / <code>mirror.conf</code>,按优先级自动回退;默认带 <code>sz</code>(默认)与 <code>main</code> 两个镜像。</p></div>
|
||||
<div class="card"><h3>Git Release 安装</h3><p><code>pmm install --git repo</code> 自动识别 GitHub / GitLab / Gitea / Forgejo 及私有部署,按平台挑资产。</p></div>
|
||||
<div class="card"><h3>.pdm 打包格式</h3><p><code>pmm pack <dir></code> 生成含 <code>control.tar.gz</code> / <code>data.tar.gz</code> / <code>sha256sums</code> 的归档,思路同 .deb。</p></div>
|
||||
<div class="card"><h3>哈希校验</h3><p>内置 SHA-256 与 SHA-1 实现,安装前强校验下载文件完整性,防篡改。</p></div>
|
||||
<div class="card"><h3>版本选择</h3><p>支持 <code>pmm install pkg==1.2.3</code>、<code>>=</code>、<code><=</code>、<code>></code>、<code><</code> 等 pip 式约束。</p></div>
|
||||
<div class="card"><h3>进度条 & 注册表</h3><p>ASCII 下载进度条;Windows 下为已装软件写入注册表卸载项,可用“应用和功能”卸载。</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="install" class="section">
|
||||
<div class="wrap">
|
||||
<h2 class="section-title">安装 <span class="accent">PMM</span></h2>
|
||||
<p class="section-sub">下载对应平台二进制,一条命令搞定,完成后自动加入 PATH。</p>
|
||||
<div class="tabs" data-tabs>
|
||||
<button class="tab active" data-tab="win">Windows</button>
|
||||
<button class="tab" data-tab="linux">Linux</button>
|
||||
<button class="tab" data-tab="macos">macOS</button>
|
||||
</div>
|
||||
<div class="code-box" data-pane="win" data-active="1">
|
||||
<pre><code># ZIP 免安装版(解压即用,pmm.exe 在压缩包内)
|
||||
curl -L -o pmm.zip https://pmm.parlz.com/downloads/pmm-<?php echo $VERSION; ?>-windows-amd64.zip
|
||||
# 解压后把 pmm.exe 所在目录加入 PATH,然后:
|
||||
pmm -v</code></pre>
|
||||
<button class="copy-btn" data-copy="win">复制</button>
|
||||
</div>
|
||||
<div class="code-box" data-pane="linux">
|
||||
<pre><code># 方式一:sh 一键安装(自动下载二进制 + 加 PATH)
|
||||
curl -sSL https://pmm.parlz.com/install.sh | bash
|
||||
|
||||
# 方式二:.deb 安装
|
||||
curl -L -o pmm.deb https://pmm.parlz.com/downloads/pmm_<?php echo $VERSION; ?>_amd64.deb
|
||||
sudo apt install ./pmm.deb
|
||||
pmm -v</code></pre>
|
||||
<button class="copy-btn" data-copy="linux">复制</button>
|
||||
</div>
|
||||
<div class="code-box" data-pane="macos">
|
||||
<pre><code># sh 一键安装
|
||||
curl -sSL https://pmm.parlz.com/install.sh | bash
|
||||
pmm -v</code></pre>
|
||||
<button class="copy-btn" data-copy="macos">复制</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="download" class="section alt">
|
||||
<div class="wrap">
|
||||
<h2 class="section-title">下载 <span class="accent">PMM</span></h2>
|
||||
<p class="section-sub">可从官方镜像站或 GitHub Release 获取。镜像地址不变。</p>
|
||||
<div class="dl-grid">
|
||||
<a class="dl-card" href="downloads/pmm-<?php echo $VERSION; ?>-windows-amd64.zip" download>
|
||||
<span class="dl-os">Windows</span><span class="dl-file">pmm-<?php echo $VERSION; ?>-windows-amd64.zip · 免安装</span><span class="dl-arrow">↓</span>
|
||||
<div class="entry-grid">
|
||||
<a class="entry-card" href="features.php">
|
||||
<span class="entry-icon">⌘</span>
|
||||
<span class="entry-title">特性</span>
|
||||
<span class="entry-desc">apt 式多镜像、git release、.pdm、依赖解析、并发下载</span>
|
||||
</a>
|
||||
<a class="dl-card" href="downloads/pmm_<?php echo $VERSION; ?>_amd64.deb" download>
|
||||
<span class="dl-os">Linux</span><span class="dl-file">pmm_<?php echo $VERSION; ?>_amd64.deb</span><span class="dl-arrow">↓</span>
|
||||
<a class="entry-card" href="install.php">
|
||||
<span class="entry-icon">▸</span>
|
||||
<span class="entry-title">安装</span>
|
||||
<span class="entry-desc">三条命令装好 Windows / Linux / macOS 客户端</span>
|
||||
</a>
|
||||
<a class="dl-card" href="install.sh" download>
|
||||
<span class="dl-os">Linux/macOS</span><span class="dl-file">install.sh · 一键安装</span><span class="dl-arrow">↓</span>
|
||||
<a class="entry-card" href="download.php">
|
||||
<span class="entry-icon">↓</span>
|
||||
<span class="entry-title">下载</span>
|
||||
<span class="entry-desc">zip / deb / install.sh / .pdm 全部安装产物</span>
|
||||
</a>
|
||||
<a class="dl-card" href="https://github.com/JGZYES/ParlzPackageManger/releases/latest" target="_blank" rel="noopener">
|
||||
<span class="dl-os">全部</span><span class="dl-file">GitHub Releases / 源码</span><span class="dl-arrow">↗</span>
|
||||
<a class="entry-card" href="servers.php">
|
||||
<span class="entry-icon">◉</span>
|
||||
<span class="entry-title">服务状态</span>
|
||||
<span class="entry-desc">深圳 / 香港镜像的实时探测与详细信息</span>
|
||||
</a>
|
||||
</div>
|
||||
<p class="tips">Linux 也可 <code>curl -sSL https://pmm.parlz.com/install.sh | bash</code>;<code>.pdm</code> 包仍可从镜像 <code>pmm install pmm</code> / <code>pmm install php</code> 安装。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="mirror" class="section">
|
||||
<div class="wrap">
|
||||
<h2 class="section-title">镜像 <span class="accent">源</span></h2>
|
||||
<p class="section-sub">PMM 默认镜像(apt 式,按优先级回退),地址与 <code>mirror.ini</code> 一致。</p>
|
||||
<div class="mirror-list">
|
||||
<div class="mirror-item"><span class="mirror-tag default">sz · 默认</span><code>https://sz.pmm.parlz.com/mirror/packages</code></div>
|
||||
<div class="mirror-item"><span class="mirror-tag">main · 备份</span><code>https://pmm.parlz.com/mirror/packages</code></div>
|
||||
</div>
|
||||
<a class="btn btn-ghost" href="mirror/" target="_blank" rel="noopener">浏览镜像目录 ↗</a>
|
||||
<p class="tips"><strong>快速开始:</strong><br><code>pmm install nodejs</code> — Node.js<br><code>pmm install php</code> — PHP<br><code>pmm install pmm</code> — 升级 PMM 自身</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="wrap">
|
||||
<div class="footer-top"><span class="brand-name">ParlzPackageManager</span><span>GNU GPL-3.0 · C11 · 跨平台</span></div>
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/JGZYES/ParlzPackageManger" target="_blank" rel="noopener">GitHub</a>
|
||||
<a href="https://github.com/JGZYES/ParlzPackageManger/releases" target="_blank" rel="noopener">Releases</a>
|
||||
<a href="#top">回到顶部</a>
|
||||
</div>
|
||||
<p class="footer-note">© 2026 ParlzPackageManager Project · 深圳 sz.pmm.parlz.com · 香港 pmm.parlz.com</p>
|
||||
</div>
|
||||
</footer>
|
||||
<p class="tips" style="padding-bottom:40px"><code>pmm install nodejs</code> · <code>pmm install php</code> · <code>pmm install pmm</code>(升级自身)</p>
|
||||
|
||||
<script>
|
||||
window.PMM_MAP = <?php echo json_encode($dots); ?>;
|
||||
window.PMM_DIMS = { rows: <?php echo $ROWS; ?>, cols: <?php echo $COLS; ?> };
|
||||
</script>
|
||||
<script src="assets/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<?php pmm_footer(); ?>
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
pmm_header('install', '安装');
|
||||
pmm_page_open('INSTALL', '安装 PMM');
|
||||
?>
|
||||
|
||||
<section class="section">
|
||||
<div class="wrap">
|
||||
<p class="section-sub">下载对应平台二进制,一条命令搞定,完成后自动加入 PATH。</p>
|
||||
<div class="tabs" data-tabs>
|
||||
<button class="tab active" data-tab="win">Windows</button>
|
||||
<button class="tab" data-tab="linux">Linux</button>
|
||||
<button class="tab" data-tab="macos">macOS</button>
|
||||
</div>
|
||||
<div class="code-box" data-pane="win" data-active="1">
|
||||
<pre><code># ZIP 免安装版(解压即用,pmm.exe 在压缩包内)
|
||||
curl -L -o pmm.zip https://pmm.parlz.com/downloads/pmm-<?php echo PMM_VERSION; ?>-windows-amd64.zip
|
||||
# 解压后把 pmm.exe 所在目录加入 PATH,然后:
|
||||
pmm -v</code></pre>
|
||||
<button class="copy-btn" data-copy="win">复制</button>
|
||||
</div>
|
||||
<div class="code-box" data-pane="linux">
|
||||
<pre><code># 方式一:sh 一键安装(自动下载二进制 + 加 PATH)
|
||||
curl -sSL https://pmm.parlz.com/install.sh | bash
|
||||
|
||||
# 方式二:.deb 安装
|
||||
curl -L -o pmm.deb https://pmm.parlz.com/downloads/pmm_<?php echo PMM_VERSION; ?>_amd64.deb
|
||||
sudo apt install ./pmm.deb
|
||||
pmm -v</code></pre>
|
||||
<button class="copy-btn" data-copy="linux">复制</button>
|
||||
</div>
|
||||
<div class="code-box" data-pane="macos">
|
||||
<pre><code># sh 一键安装
|
||||
curl -sSL https://pmm.parlz.com/install.sh | bash
|
||||
pmm -v</code></pre>
|
||||
<button class="copy-btn" data-copy="macos">复制</button>
|
||||
</div>
|
||||
|
||||
<h2 class="section-title" style="margin-top:56px;font-size:24px">装好之后</h2>
|
||||
<div class="code-box">
|
||||
<pre><code>pmm search node # 在镜像里搜包
|
||||
pmm install nodejs # 安装(自动解析依赖)
|
||||
pmm install nodejs==24.20.0 # 指定版本
|
||||
pmm install --git owner/repo@v1.2.3 # 从 git release 安装指定 tag
|
||||
pmm list # 已装列表
|
||||
pmm remove nodejs # 卸载</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php pmm_footer(); ?>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
pmm_header('status', '服务状态');
|
||||
pmm_page_open('SERVER STATUS', '服务状态');
|
||||
?>
|
||||
|
||||
<section class="section" style="padding-top:24px">
|
||||
<div class="wrap">
|
||||
<p class="section-sub">实时监控 PMM 双镜像(深圳 / 香港)的注册表、下载服务与 Web 浏览三项能力,每 15 秒自动刷新。不暴露任何服务器 IP。</p>
|
||||
|
||||
<div class="status-toolbar">
|
||||
<span class="status-updated" id="status-updated">检测中…</span>
|
||||
<button class="btn btn-ghost btn-sm" id="status-refresh" type="button">手动刷新</button>
|
||||
</div>
|
||||
|
||||
<div class="svc-grid" id="svc-grid">
|
||||
<div class="status-card"><div class="status-head"><span class="pin">深圳</span><span class="status-dot" id="dot-0"></span></div><div class="status-ip">sz.pmm.parlz.com</div><div class="status-host">https://sz.pmm.parlz.com/mirror/packages</div><div class="status-meta" id="meta-0">检测中…</div></div>
|
||||
<div class="status-card"><div class="status-head"><span class="pin">香港</span><span class="status-dot" id="dot-1"></span></div><div class="status-ip">pmm.parlz.com</div><div class="status-host">https://pmm.parlz.com/mirror/packages</div><div class="status-meta" id="meta-1">检测中…</div></div>
|
||||
</div>
|
||||
|
||||
<div id="svc-detail"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php pmm_footer(); ?>
|
||||
+56
-48
@@ -1,64 +1,72 @@
|
||||
<?php
|
||||
/* Status probe for PMM mirror servers.
|
||||
* Server-side GET (curl -k, since we hit IPs/hosts that may not match the
|
||||
* browser's TLS expectations) -> returns JSON for the homepage status panel. */
|
||||
/* Detailed status probe for the PMM mirror servers.
|
||||
* For each mirror we probe three endpoints (registry index / package download /
|
||||
* web dir listing) and report HTTP code, latency, server software and a
|
||||
* timestamp. Output is JSON for servers.php. No server IPs are exposed. */
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-store');
|
||||
|
||||
$servers = [
|
||||
['name'=>'深圳', 'url'=>'https://sz.pmm.parlz.com/mirror/packages/pmm.json'],
|
||||
['name'=>'香港', 'url'=>'https://pmm.parlz.com/mirror/packages/pmm.json'],
|
||||
['name' => '深圳', 'region' => 'CN · 深圳', 'host' => 'sz.pmm.parlz.com'],
|
||||
['name' => '香港', 'region' => 'HK · 香港', 'host' => 'pmm.parlz.com'],
|
||||
];
|
||||
|
||||
function probe($url) {
|
||||
$t = microtime(true);
|
||||
/* 1) PHP curl extension (preferred) */
|
||||
if (function_exists('curl_init')) {
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_NOBODY => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 3,
|
||||
CURLOPT_TIMEOUT => 6,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_USERAGENT => 'pmm-status/1.0',
|
||||
]);
|
||||
curl_exec($ch);
|
||||
$code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$errno = curl_errno($ch);
|
||||
curl_close($ch);
|
||||
if ($errno === 0 && $code > 0)
|
||||
return ['online'=>$code < 500, 'code'=>$code, 'ms'=>(int)round((microtime(true)-$t)*1000)];
|
||||
/* Probe one URL with shell curl: returns [code, ms, server, ctype]. */
|
||||
function probe_url(string $url): array {
|
||||
$cmd = 'curl -k -s -o /dev/null -D - --max-time 8 '
|
||||
. '-w "__PMM_PROBE__%{http_code} %{time_total}" ' . escapeshellarg($url);
|
||||
$out = (string)@shell_exec($cmd);
|
||||
$code = 0; $ms = 0; $server = ''; $ctype = '';
|
||||
if ($out !== '') {
|
||||
/* headers before the marker, "code ms" after it */
|
||||
$pos = strrpos($out, '__PMM_PROBE__');
|
||||
if ($pos !== false) {
|
||||
$meta = trim(substr($out, $pos + strlen('__PMM_PROBE__')));
|
||||
$parts = preg_split('/\s+/', $meta);
|
||||
if (isset($parts[0])) $code = (int)$parts[0];
|
||||
if (isset($parts[1])) $ms = (int)round(((float)$parts[1]) * 1000);
|
||||
$head = substr($out, 0, $pos);
|
||||
if (preg_match('/^Server:\s*(.+)$/mi', $head, $m)) $server = trim($m[1]);
|
||||
if (preg_match('/^Content-Type:\s*(.+)$/mi', $head, $m)) $ctype = trim($m[1]);
|
||||
}
|
||||
}
|
||||
/* 2) shell curl (pmm depends on the curl binary, so it is present) */
|
||||
if (function_exists('shell_exec') && !in_array('shell_exec', array_map('trim', explode(',', ini_get('disable_functions'))))) {
|
||||
$cmd = 'curl -k -s -o /dev/null -w "%{http_code}" --connect-timeout 3 --max-time 6 ' . escapeshellarg($url);
|
||||
$out = trim((string)@shell_exec($cmd));
|
||||
$code = (int)$out;
|
||||
if ($code > 0)
|
||||
return ['online'=>$code < 500, 'code'=>$code, 'ms'=>(int)round((microtime(true)-$t)*1000)];
|
||||
}
|
||||
/* 3) stream context fallback */
|
||||
$ctx = stream_context_create([
|
||||
'http' => ['method'=>'HEAD','timeout'=>6,'follow_location'=>1,'ignore_errors'=>true],
|
||||
'ssl' => ['verify_peer'=>false,'verify_peer_name'=>false],
|
||||
]);
|
||||
$fp = @fopen($url, 'r', false, $ctx);
|
||||
$ms = (int)round((microtime(true) - $t) * 1000);
|
||||
if ($fp) { fclose($fp); return ['online'=>true,'code'=>200,'ms'=>$ms]; }
|
||||
return ['online'=>false,'code'=>0,'ms'=>$ms];
|
||||
return [$code, $ms, $server, $ctype];
|
||||
}
|
||||
|
||||
function probe_check(string $url): array {
|
||||
[$code, $ms, $server, $ctype] = probe_url($url);
|
||||
return [
|
||||
'ok' => ($code >= 200 && $code < 500 && $code !== 0),
|
||||
'code' => $code,
|
||||
'ms' => $ms,
|
||||
'server' => $server,
|
||||
'ctype' => $ctype,
|
||||
];
|
||||
}
|
||||
|
||||
$out = [];
|
||||
foreach ($servers as $s) {
|
||||
$st = probe($s['url']);
|
||||
$h = $s['host'];
|
||||
$reg = probe_check("https://{$h}/mirror/packages/pmm.json");
|
||||
$dl = probe_check("https://{$h}/mirror/packages/pmm/" . PMM_VERSION . "-linux-amd64.pdm");
|
||||
$web = probe_check("https://{$h}/mirror/");
|
||||
$avg = 0; $n = 0;
|
||||
foreach ([$reg, $dl, $web] as $c) if ($c['ok']) { $avg += $c['ms']; $n++; }
|
||||
$avg = $n ? (int)round($avg / $n) : 0;
|
||||
$out[] = [
|
||||
'name' => $s['name'],
|
||||
'online' => $st['online'],
|
||||
'code' => $st['code'],
|
||||
'ms' => $st['ms'],
|
||||
'name' => $s['name'],
|
||||
'region' => $s['region'],
|
||||
'host' => $h,
|
||||
'online' => $reg['ok'],
|
||||
'avg_ms' => $avg,
|
||||
'checks' => [
|
||||
['item' => '注册表 (packages API)', 'url' => "https://{$h}/mirror/packages/pmm.json", 'result' => $reg],
|
||||
['item' => '下载服务 (.pdm 分发)', 'url' => "https://{$h}/mirror/packages/pmm/" . PMM_VERSION . "-linux-amd64.pdm", 'result' => $dl],
|
||||
['item' => 'Web 目录浏览', 'url' => "https://{$h}/mirror/", 'result' => $web],
|
||||
],
|
||||
'checked_at' => gmdate('Y-m-d\TH:i:s\Z'),
|
||||
];
|
||||
}
|
||||
echo json_encode($out, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
在新工单中引用
屏蔽一个用户