镜像自地址
https://github.com/JGZYES/ParlzPackageManger.git
已同步 2026-09-11 05:42:44 +08:00
web: refactor to minimal homepage + docs site
- New minimal index.php: brand hero (+ rotating Earth canvas) + entry cards to docs; no multi-page nav. - New web/docs/ Markdown site: docs/index.php renders sibling .md via the shared md() renderer (moved into _common.php). Pages: index/install/download/features/ cli/mirror/translate/source. - _common.php: slim nav (首页/文档), add md(), keep PMM_VERSION + layout. - Delete old multi-pages (features/install/download/servers/source/git/translate) and the release/asset serving (releases/, downloads/, root install.sh, status.php). Keep web/mirror/ (package registry + lang packs) so the pmm CLI and docs download links (GitHub Release) still work. - assets/style.css: add docs styling. mirror/index.php: fix deprecation. - Fix docs install/download links to point at GitHub Release (releases/ removed). Verified: php -l passes for _common/index/docs/mirror; homepage (globe+hero), docs pages and mirror render via the PHP built-in server; no stale references to deleted pages / releases / downloads / status remain.
这个提交包含在:
+17
-7
@@ -8,13 +8,8 @@ define('PMM_VERSION', '0.3.6');
|
||||
|
||||
function pmm_nav_links(string $active): string {
|
||||
$items = [
|
||||
'home' => ['index.php', '首页'],
|
||||
'features' => ['features.php', '特性'],
|
||||
'install' => ['install.php', '安装'],
|
||||
'download' => ['download.php', '下载'],
|
||||
'status' => ['servers.php', '服务状态'],
|
||||
'translate'=> ['translate.php', '翻译'],
|
||||
'source' => ['git.php', '源码'],
|
||||
'home' => ['index.php', '首页'],
|
||||
'docs' => ['docs/index.php', '文档'],
|
||||
];
|
||||
$html = '';
|
||||
foreach ($items as $key => [$href, $label]) {
|
||||
@@ -24,6 +19,21 @@ function pmm_nav_links(string $active): string {
|
||||
return $html;
|
||||
}
|
||||
|
||||
/* Minimal markdown renderer (self-contained). Used by the docs site. */
|
||||
function md(string $t): string {
|
||||
$t = htmlspecialchars($t, ENT_QUOTES, 'UTF-8');
|
||||
$t = preg_replace('/```([a-zA-Z0-9]*)\n(.*?)```/s', "<pre class=\"mdcode\"><code>\\2</code></pre>", $t);
|
||||
$t = preg_replace('/^### (.*)$/m', '<h3>$1</h3>', $t);
|
||||
$t = preg_replace('/^## (.*)$/m', '<h2>$1</h2>', $t);
|
||||
$t = preg_replace('/^# (.*)$/m', '<h1>$1</h1>', $t);
|
||||
$t = preg_replace('/^[-*] (.*)$/m', '<li>$1</li>', $t);
|
||||
$t = preg_replace('/^\s*---\s*$/m', '<hr>', $t);
|
||||
$t = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $t);
|
||||
$t = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', '<a href="$2" target="_blank" rel="noopener">$1</a>', $t);
|
||||
$t = preg_replace('/`([^`]+)`/', '<code>$1</code>', $t);
|
||||
return $t;
|
||||
}
|
||||
|
||||
function pmm_header(string $active, string $title): void {
|
||||
$t = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
|
||||
echo '<!DOCTYPE html>' . "\n" .
|
||||
|
||||
@@ -297,3 +297,26 @@ code{font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,"Co
|
||||
.rel-size{font-size:11.5px;color:var(--dim);font-family:ui-monospace,monospace;white-space:nowrap}
|
||||
.rel-dl{margin-left:auto;font-size:12px;color:var(--muted);border:1px solid var(--line);border-radius:6px;padding:3px 10px;text-decoration:none;white-space:nowrap}
|
||||
.rel-dl:hover{color:#fff;border-color:#fff}
|
||||
|
||||
/* ---------- docs site ---------- */
|
||||
.docs-nav{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:24px;border-bottom:1px solid var(--line);padding-bottom:14px}
|
||||
.docs-link{font-size:13.5px;color:var(--muted);text-decoration:none;padding:6px 14px;border:1px solid var(--line);border-radius:999px;white-space:nowrap}
|
||||
.docs-link:hover{color:#fff;border-color:#fff}
|
||||
.docs-link.active{color:var(--ink);background:var(--accent);border-color:var(--accent)}
|
||||
.docs-body{max-width:860px;line-height:1.8;color:var(--text)}
|
||||
.docs-body h1{font-size:30px;margin:0 0 18px;letter-spacing:-.5px;border-bottom:1px solid var(--line);padding-bottom:14px}
|
||||
.docs-body h2{font-size:20px;margin:34px 0 12px;color:#fff}
|
||||
.docs-body h3{font-size:16px;margin:26px 0 10px;color:#fff}
|
||||
.docs-body p{margin:0 0 14px}
|
||||
.docs-body li{margin:5px 0}
|
||||
.docs-body ul,.docs-body ol{padding-left:22px;margin:0 0 14px}
|
||||
.docs-body code{background:var(--panel);border:1px solid var(--line);padding:1px 6px;border-radius:5px;font-size:13px;font-family:ui-monospace,monospace;color:var(--muted)}
|
||||
.docs-body pre.mdcode{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:14px 16px;overflow:auto;margin:0 0 14px}
|
||||
.docs-body pre.mdcode code{background:none;border:0;padding:0;color:var(--text);font-size:13px;line-height:1.6;display:block}
|
||||
.docs-body hr{border:0;border-top:1px solid var(--line);margin:22px 0}
|
||||
.docs-body a{color:#fff;text-decoration:underline}
|
||||
.docs-body a:hover{color:var(--accent)}
|
||||
.docs-body strong{color:#fff}
|
||||
.docs-body table{border-collapse:collapse;width:100%;margin:0 0 16px}
|
||||
.docs-body th,.docs-body td{border:1px solid var(--line);padding:8px 12px;font-size:13px;text-align:left}
|
||||
.docs-body th{background:var(--panel);color:#fff}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# 命令参考
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
pmm install <pkg> # 从注册表安装
|
||||
pmm install <pkg> [pkg2 ...] # 装多个
|
||||
pmm install <file.deb|.rpm|.apk|.pdm> # 装本地包
|
||||
pmm install -dpkg <x.deb> # 强制按 deb 装
|
||||
pmm install -rpm <x.rpm> # 强制按 rpm 装
|
||||
pmm install --git <repo> # 从 git Release 装
|
||||
pmm install --force <pkg> # 强制重装
|
||||
```
|
||||
|
||||
## 卸载 / 查询
|
||||
|
||||
```bash
|
||||
pmm remove <pkg>
|
||||
pmm list # 已安装
|
||||
pmm list --upgradable # 可升级的
|
||||
pmm search <keyword> # 搜索
|
||||
pmm info <package|file.pdm> # 详情
|
||||
pmm verify <file> # sha256/sha1
|
||||
```
|
||||
|
||||
## 镜像 / 更新
|
||||
|
||||
```bash
|
||||
pmm mirror list
|
||||
pmm mirror add <n> <api>
|
||||
pmm mirror use <n>
|
||||
pmm mirror remove <n>
|
||||
pmm mirror check # 探测各镜像源可达性
|
||||
pmm update # 刷新镜像索引
|
||||
pmm upgrade [--yes] # 升级所有已装包
|
||||
pmm self-update # 更新 pmm 自身
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
```bash
|
||||
pmm setting lang -l # 列语言包
|
||||
pmm setting lang <locale> # 切换语言
|
||||
pmm setting set <key> <value> # 写 pmm.conf(如 mirror / proxy)
|
||||
pmm setting get # 读 pmm.conf
|
||||
pmm setting mirror <name> # 设当前镜像
|
||||
pmm cache clean # 清缓存
|
||||
```
|
||||
|
||||
## 全局选项
|
||||
|
||||
```bash
|
||||
--no-color # 禁用 ANSI 颜色
|
||||
-q, --quiet # 只显示错误/警告
|
||||
--verbose # 更多细节
|
||||
-p<drive> # 指定安装盘符(Windows)
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
# 下载
|
||||
|
||||
PMM 提供多种安装包。官网镜像站保留二进制注册表(`mirror/`),也可直接从 GitHub Release 获取。
|
||||
|
||||
## 一键安装(推荐)
|
||||
|
||||
```bash
|
||||
curl -sSL https://pmm.parlz.com/install.sh | bash
|
||||
```
|
||||
|
||||
> 说明:`install.sh` 现在由 GitHub Release 提供;若你手动部署,可把仓库里的 `install.sh` 放到站点根目录,或直接用下方的 GitHub 链接。
|
||||
|
||||
## 各平台产物(GitHub Release)
|
||||
|
||||
- [pmm-0.3.6-windows-amd64.zip](https://github.com/JGZYES/ParlzPackageManger/releases/latest) — Windows 免安装
|
||||
- [pmm_0.3.6_amd64.deb](https://github.com/JGZYES/ParlzPackageManger/releases/latest) — Debian/Ubuntu
|
||||
- [pmm-0.3.6.x86_64.rpm](https://github.com/JGZYES/ParlzPackageManger/releases/latest) — CentOS/RHEL
|
||||
- [pmm-0.3.6-linux-amd64.pdm](https://github.com/JGZYES/ParlzPackageManger/releases/latest) — `.pdm` 包
|
||||
- [pmm-0.3.6.apk](https://github.com/JGZYES/ParlzPackageManger/releases/latest) — Alpine
|
||||
- [pmm-0.3.6.pkg.tar.zst](https://github.com/JGZYES/ParlzPackageManger/releases/latest) — Arch
|
||||
|
||||
## 用 pmm 安装(从镜像注册表)
|
||||
|
||||
mirror 注册表保留在 `web/mirror/`,pmm 客户端可直接安装:
|
||||
|
||||
```bash
|
||||
pmm install pmm==0.3.6 # 升级 pmm 自身
|
||||
pmm install nodejs # 装一个软件包
|
||||
```
|
||||
|
||||
## 镜像源
|
||||
|
||||
- **深圳(默认)**:`https://sz.pmm.parlz.com/mirror/packages`
|
||||
- **香港(备份)**:`https://pmm.parlz.com/mirror/packages`
|
||||
|
||||
全部历史版本的 `.pdm` 都在镜像 `mirror/packages/pmm/` 下。详见 [镜像源](index.php?page=mirror)。
|
||||
@@ -0,0 +1,15 @@
|
||||
# 核心特性
|
||||
|
||||
- **apt 式多镜像源**:按优先级回退,`mirror.ini` 可配多个 registry。
|
||||
- **Git Release 安装**:直接装 GitHub / GitLab / Gitea / Forgejo 的最新 Release。
|
||||
- **`.pdm` 打包格式**:deb 风格的内置包管理器,跨平台。
|
||||
- **依赖解析**:安装时自动解析 `Depends`。
|
||||
- **并发下载 + 断点续传**:大文件分片并行、断点续传。
|
||||
- **哈希校验**:下载后 sha256 / sha1 校验,拒绝校验失败。
|
||||
- **本地包安装**:`.deb` / `.rpm` / `.apk` / `.msi` / `.zip` / `.pkg.tar.zst` 都能装。
|
||||
- **搜索与信息**:`pmm search` / `pmm info`。
|
||||
- **缓存管理**:`pmm cache clean`。
|
||||
- **彩色输出 + 语言包**:`[PMM]:[LEVEL]` 结构化输出,`.pjson` 多语言。
|
||||
- **i18n**:`pmm setting lang <locale>` 切换语言,社区可贡献。
|
||||
|
||||
详见 [命令参考](index.php?page=cli) 与 [翻译](index.php?page=translate)。
|
||||
@@ -0,0 +1,34 @@
|
||||
# 快速开始
|
||||
|
||||
**PMM(ParlzPackageManager)** 是一个用 C 语言从零编写的跨平台包管理器,理念:**敲即得,装即跑**。
|
||||
|
||||
支持 Windows / Linux / macOS。像 apt 一样多镜像源,也能直接装 GitHub / GitLab / Gitea / Forgejo 的 Release。
|
||||
|
||||
## 一分钟装好
|
||||
|
||||
```bash
|
||||
curl -sSL https://pmm.parlz.com/install.sh | bash
|
||||
```
|
||||
|
||||
装好后:
|
||||
|
||||
```bash
|
||||
pmm --version # 查看版本
|
||||
pmm install nodejs # 安装一个软件包
|
||||
pmm list # 已安装的软件包
|
||||
```
|
||||
|
||||
## 常用命令一览
|
||||
|
||||
```bash
|
||||
pmm install <pkg> 安装
|
||||
pmm remove <pkg> 卸载
|
||||
pmm search <kw> 搜索
|
||||
pmm info <pkg> 详情
|
||||
pmm update 刷新镜像索引
|
||||
pmm upgrade 升级所有已装包
|
||||
pmm mirror list 查看镜像源
|
||||
pmm setting lang <xx> 切换语言包
|
||||
```
|
||||
|
||||
详见 [命令参考](index.php?page=cli)。
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/* PMM docs site — renders sibling .md files with the shared md() renderer.
|
||||
* URL: docs/index.php?page=<name> (defaults to "index"). */
|
||||
define('PMM_SITE', 1);
|
||||
require dirname(__DIR__) . '/_common.php';
|
||||
|
||||
/* doc pages: [slug, title] */
|
||||
$pages = [
|
||||
'index' => '快速开始',
|
||||
'install' => '安装',
|
||||
'download' => '下载',
|
||||
'features' => '核心特性',
|
||||
'cli' => '命令参考',
|
||||
'mirror' => '镜像源',
|
||||
'translate' => '翻译 / 语言包',
|
||||
'source' => '源码',
|
||||
];
|
||||
$page = isset($_GET['page']) ? (string)$_GET['page'] : 'index';
|
||||
if (!isset($pages[$page])) $page = 'index';
|
||||
$title = $pages[$page];
|
||||
|
||||
$mdPath = __DIR__ . '/' . $page . '.md';
|
||||
$body = '';
|
||||
if (is_file($mdPath)) {
|
||||
$body = (string)file_get_contents($mdPath);
|
||||
$body = str_replace("\r", "", $body); // md() regex hard-codes \n
|
||||
$body = md($body);
|
||||
}
|
||||
|
||||
pmm_header('docs', '文档 · ' . $title);
|
||||
pmm_page_open('DOCS', $title);
|
||||
?>
|
||||
<section class="section">
|
||||
<div class="wrap">
|
||||
<nav class="docs-nav">
|
||||
<?php foreach ($pages as $slug => $t): ?>
|
||||
<a class="docs-link<?php echo $slug === $page ? ' active' : ''; ?>" href="index.php?page=<?php echo $slug; ?>"><?php echo htmlspecialchars($t); ?></a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
<div class="docs-body"><?php echo $body; ?></div>
|
||||
</div>
|
||||
</section>
|
||||
<?php pmm_footer(); ?>
|
||||
@@ -0,0 +1,49 @@
|
||||
# 安装
|
||||
|
||||
## Linux / macOS 一键
|
||||
|
||||
```bash
|
||||
curl -sSL https://pmm.parlz.com/install.sh | bash
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
下载并解压 zip,将 `pmm.exe` 加入 PATH:
|
||||
|
||||
```bash
|
||||
# PowerShell
|
||||
Invoke-WebRequest https://github.com/JGZYES/ParlzPackageManger/releases/latest/download/pmm-0.3.6-windows-amd64.zip -OutFile pmm.zip
|
||||
Expand-Archive pmm.zip -DestinationPath pmm
|
||||
# 然后将 pmm\pmm.exe 所在目录加入 PATH
|
||||
```
|
||||
|
||||
## Debian / Ubuntu(.deb)
|
||||
|
||||
```bash
|
||||
curl -L -o pmm.deb https://github.com/JGZYES/ParlzPackageManger/releases/latest/download/pmm_0.3.6_amd64.deb
|
||||
sudo apt install ./pmm.deb
|
||||
```
|
||||
|
||||
## CentOS / RHEL(.rpm)
|
||||
|
||||
```bash
|
||||
curl -L -o pmm.rpm https://github.com/JGZYES/ParlzPackageManger/releases/latest/download/pmm-0.3.6-1.x86_64.rpm
|
||||
sudo rpm -Uvh pmm.rpm
|
||||
```
|
||||
|
||||
## 用 pmm 安装 pmm 自身
|
||||
|
||||
```bash
|
||||
pmm install pmm==0.3.6
|
||||
```
|
||||
|
||||
## 装好后
|
||||
|
||||
```bash
|
||||
pmm search nodejs
|
||||
pmm install nodejs
|
||||
pmm list
|
||||
pmm remove nodejs
|
||||
```
|
||||
|
||||
详见 [命令参考](index.php?page=cli) 与 [下载](index.php?page=download)。
|
||||
@@ -0,0 +1,40 @@
|
||||
# 镜像源
|
||||
|
||||
PMM 采用 apt 式多镜像源,按优先级回退。配置在 `~/.pmm/mirror.ini`。
|
||||
|
||||
## 默认镜像
|
||||
|
||||
- **深圳(默认)**:`https://sz.pmm.parlz.com/mirror/packages`
|
||||
- **香港(备份)**:`https://pmm.parlz.com/mirror/packages`
|
||||
|
||||
## mirror.ini 格式
|
||||
|
||||
```ini
|
||||
[sz]
|
||||
registry = https://sz.pmm.parlz.com/mirror/packages
|
||||
priority = 1
|
||||
default = true
|
||||
|
||||
[main]
|
||||
registry = https://pmm.parlz.com/mirror/packages
|
||||
priority = 20
|
||||
```
|
||||
|
||||
## 管理镜像
|
||||
|
||||
```bash
|
||||
pmm mirror list # 列出
|
||||
pmm mirror add <n> <api> # 添加
|
||||
pmm mirror use <n> # 设为当前
|
||||
pmm mirror remove <n> # 移除
|
||||
pmm mirror check # 探测每个源的可达性/优先级
|
||||
```
|
||||
|
||||
## 服务状态
|
||||
|
||||
首页有深圳/香港双镜像的实时探测:
|
||||
- 注册表 `mirror/packages/pmm.json`
|
||||
- `.pdm` 下载 `mirror/packages/pmm/<ver>-linux-amd64.pdm`
|
||||
- 镜像目录浏览 `mirror/`
|
||||
|
||||
详见 [下载](index.php?page=download)。
|
||||
@@ -0,0 +1,22 @@
|
||||
# 源码
|
||||
|
||||
PMM 是开源项目(GPL-3.0)。
|
||||
|
||||
- **GitHub 仓库**:[JGZYES/ParlzPackageManger](https://github.com/JGZYES/ParlzPackageManger)
|
||||
- **GitHub Releases**:[releases](https://github.com/JGZYES/ParlzPackageManger/releases)
|
||||
- **提交 Issue / PR**:[issues](https://github.com/JGZYES/ParlzPackageManger/issues)
|
||||
|
||||
## 构建
|
||||
|
||||
需要 C11 编译器 + 静态链接环境:
|
||||
|
||||
```bash
|
||||
# Linux
|
||||
gcc -O2 -Wall -static -std=c11 -o pmm \
|
||||
src/main.c src/json.c src/ini.c src/pmm.c src/http.c src/repo.c \
|
||||
src/install.c src/sha256.c src/sha1.c src/mirrors.c src/pdm.c src/out.c src/i18n.c
|
||||
```
|
||||
|
||||
## 文档规范
|
||||
|
||||
界面文案、`.pjson` 语言包键名、版本 banner 等遵循 [STANDARD.md](https://github.com/JGZYES/ParlzPackageManger/blob/main/docs/STANDARD.md)。
|
||||
@@ -0,0 +1,32 @@
|
||||
# 翻译 / 语言包
|
||||
|
||||
PMM 的界面文案由 `.pjson` 语言包驱动。默认内置 **zh-CN** 与 **en-US**,社区可以提交新的语言包或修订译文。
|
||||
|
||||
## 安装语言包
|
||||
|
||||
```bash
|
||||
pmm setting lang -l # 列已有语言包
|
||||
pmm setting lang ja-JP # 安装并切换
|
||||
```
|
||||
|
||||
## 如何贡献一门语言
|
||||
|
||||
1. 从镜像下载一个语言包作模板(如 `zh-CN.pjson`)。
|
||||
2. 把键名保持不变、只改值,另存为你的语言,如 `ja-JP.pjson`、`zh-TW.pjson`。
|
||||
3. 将文件发到 **`luoriguodu@qq.com`**,或在 **GitHub 提 Issue**。
|
||||
4. 通过后进入镜像 `web/mirror/lang/`,用户即可 `pmm setting lang <locale>` 安装。
|
||||
|
||||
## 语言包格式
|
||||
|
||||
```json
|
||||
{
|
||||
"help.banner": "PMM %s (%s)",
|
||||
"cmd.install": "install",
|
||||
"desc.install": "根据名称安装指定软件包",
|
||||
"msg.installed": "已安装 %s"
|
||||
}
|
||||
```
|
||||
|
||||
键命名:`<区域>.<语义>`(如 `msg.err.not-found`),占位符用 `%s` / `%d`。
|
||||
|
||||
详见 [STANDARD.md](https://github.com/JGZYES/ParlzPackageManger/blob/main/docs/STANDARD.md)。
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
<?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="releases/v<?php echo PMM_VERSION; ?>/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="releases/v<?php echo PMM_VERSION; ?>/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="releases/v<?php echo PMM_VERSION; ?>/pmm-<?php echo PMM_VERSION; ?>.x86_64.rpm" download>
|
||||
<span class="dl-os">Linux</span><span class="dl-file">pmm-<?php echo PMM_VERSION; ?>.x86_64.rpm</span><span class="dl-arrow">↓</span>
|
||||
</a>
|
||||
<a class="dl-card" href="releases/v<?php echo PMM_VERSION; ?>/pmm-<?php echo PMM_VERSION; ?>-linux-amd64.pdm" download>
|
||||
<span class="dl-os">.pdm 包</span><span class="dl-file">pmm-<?php echo PMM_VERSION; ?>-linux-amd64.pdm</span><span class="dl-arrow">↓</span>
|
||||
</a>
|
||||
<a class="dl-card" href="releases/v<?php echo PMM_VERSION; ?>/pmm-<?php echo PMM_VERSION; ?>.apk" download>
|
||||
<span class="dl-os">Alpine</span><span class="dl-file">pmm-<?php echo PMM_VERSION; ?>.apk</span><span class="dl-arrow">↓</span>
|
||||
</a>
|
||||
<a class="dl-card" href="releases/v<?php echo PMM_VERSION; ?>/pmm-<?php echo PMM_VERSION; ?>.pkg.tar.zst" download>
|
||||
<span class="dl-os">Arch</span><span class="dl-file">pmm-<?php echo PMM_VERSION; ?>.pkg.tar.zst</span><span class="dl-arrow">↓</span>
|
||||
</a>
|
||||
<a class="dl-card" href="releases/v<?php echo PMM_VERSION; ?>/install.sh" download>
|
||||
<span class="dl-os">Linux/macOS</span><span class="dl-file">install.sh · 一键安装脚本</span><span class="dl-arrow">↓</span>
|
||||
</a>
|
||||
<div class="dl-card">
|
||||
<span class="dl-os">Linux/macOS</span>
|
||||
<span class="dl-file">一键安装 · 复制命令</span>
|
||||
<div class="dl-cmd"><code>curl -sSL https://pmm.parlz.com/install.sh | bash</code></div>
|
||||
<button class="copy-btn" data-copy-text="curl -sSL https://pmm.parlz.com/install.sh | bash" type="button">复制命令</button>
|
||||
</div>
|
||||
<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(); ?>
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ParlzPackageManager (PMM) — Linux/macOS one-line installer.
|
||||
#
|
||||
# 用法:
|
||||
# curl -sSL -o install.sh https://pmm.parlz.com/install.sh && bash install.sh
|
||||
# 或:curl -sSL https://pmm.parlz.com/install.sh | bash
|
||||
#
|
||||
# 下载官方 release 二进制并安装到 /usr/local/bin/pmm(root)或 ~/.local/bin/pmm。
|
||||
set -e
|
||||
VER="0.3.6"
|
||||
REPO="JGZYES/ParlzPackageManger"
|
||||
|
||||
# ---- 检测平台 ----
|
||||
OS="$(uname -s)"; ARCH="$(uname -m)"
|
||||
case "$OS" in
|
||||
Linux) os=linux ;;
|
||||
Darwin) os=macos ;;
|
||||
*) echo "pmm: 不支持的平台 $OS"; exit 1 ;;
|
||||
esac
|
||||
case "$ARCH" in
|
||||
x86_64|amd64) arch=amd64 ;;
|
||||
aarch64|arm64) arch=arm64 ;;
|
||||
*) echo "pmm: 不支持的架构 $ARCH"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# ---- 下载 pmm(release 资产名为 pmm)----
|
||||
URL="https://github.com/$REPO/releases/download/v$VER/pmm"
|
||||
if [ "$arch" != "amd64" ]; then
|
||||
echo "pmm: 注意:官方发布目前只提供 amd64 资产,$os/$arch 可能 404"
|
||||
fi
|
||||
echo "pmm: 下载 v$VER ($os/$arch) ..."
|
||||
TMP="$(mktemp)"
|
||||
# --progress-bar shows a live download progress line (tty only)
|
||||
if ! curl -fL --progress-bar --max-time 300 -o "$TMP" "$URL"; then
|
||||
rm -f "$TMP"; echo "pmm: 下载失败($URL)"; exit 1
|
||||
fi
|
||||
echo ""
|
||||
chmod +x "$TMP"
|
||||
|
||||
# ---- 安装 ----
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
DEST="/usr/local/bin/pmm"
|
||||
else
|
||||
DEST="${HOME}/.local/bin/pmm"
|
||||
fi
|
||||
mkdir -p "$(dirname "$DEST")"
|
||||
cp "$TMP" "$DEST" && chmod +x "$DEST" && rm -f "$TMP"
|
||||
|
||||
# ---- PATH 提示 ----
|
||||
case ":$PATH:" in
|
||||
*":$(dirname "$DEST"):"*) ;;
|
||||
*) echo "pmm: 请将 $(dirname "$DEST") 加入 PATH(或重新登录 shell)" ;;
|
||||
esac
|
||||
|
||||
echo "pmm: 安装完成 -> $DEST"
|
||||
"$DEST" -v 2>/dev/null || true
|
||||
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
-24
@@ -1,24 +0,0 @@
|
||||
<?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(); ?>
|
||||
-366
@@ -1,366 +0,0 @@
|
||||
<?php
|
||||
/* git.php — GitHub-style browser for a LOCAL stored copy of the PMM repository.
|
||||
* Reads the git repo at $REPO_DIR (default web/repo/), a local clone stored on
|
||||
* the host. No online clone on view; if the repo isn't stored yet, show setup
|
||||
* instructions + a one-click clone/pull. */
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
|
||||
$REPO_URL = 'https://github.com/JGZYES/ParlzPackageManger.git';
|
||||
/* Local repo clone lives in web/git (i.e. /www/sites/ParlzPackageManger/index/web/git
|
||||
* on the live host). PMM_SRC_DIR may override it. PHP needs write access there. */
|
||||
$REPO_DIR = getenv('PMM_SRC_DIR') ?: (__DIR__ . '/git');
|
||||
|
||||
/* handle the one-click clone/pull early (no page output yet) */
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'init') {
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
if (!is_dir($REPO_DIR)) @mkdir($REPO_DIR, 0775, true);
|
||||
if (!is_dir($REPO_DIR . '/.git')) {
|
||||
$cmd = 'git clone --depth 1 ' . escapeshellarg($REPO_URL) . ' ' . escapeshellarg($REPO_DIR) . ' 2>&1';
|
||||
$out = (string)@shell_exec($cmd);
|
||||
if (is_dir($REPO_DIR . '/.git')) echo 'OK';
|
||||
else echo '克隆失败:' . substr($out, 0, 220) . "\n(请确保 PHP 对 " . $REPO_DIR . " 可写:chmod -R 775 " . $REPO_DIR . " 或其父目录;或设 PMM_SRC_DIR)";
|
||||
} else {
|
||||
$out = (string)@shell_exec('git -C ' . escapeshellarg($REPO_DIR) . ' pull --depth 1 2>&1');
|
||||
echo 'OK (pull: ' . substr(trim($out), 0, 80) . ')';
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
$repo_ok = is_dir($REPO_DIR . '/.git');
|
||||
|
||||
function sh(string $c): string { return trim((string)@shell_exec($c)); }
|
||||
function repo_path(string $rel): string {
|
||||
global $REPO_DIR;
|
||||
$base = realpath($REPO_DIR);
|
||||
$full = realpath($REPO_DIR . '/' . $rel);
|
||||
if (!$base || !$full) return '';
|
||||
if (strpos(str_replace('\\', '/', $full), str_replace('\\', '/', $base)) !== 0) return '';
|
||||
return $full;
|
||||
}
|
||||
function esc(string $s): string { return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); }
|
||||
function fmt_size(int $b): string {
|
||||
if ($b < 1024) return $b . ' B';
|
||||
if ($b < 1048576) return round($b / 1024, 1) . ' KB';
|
||||
return round($b / 1048576, 1) . ' MB';
|
||||
}
|
||||
function md(string $t): string {
|
||||
$t = htmlspecialchars($t, ENT_QUOTES, 'UTF-8');
|
||||
$t = preg_replace('/```([a-zA-Z0-9]*)\n(.*?)```/s', "<pre class=\"mdcode\"><code>\\2</code></pre>", $t);
|
||||
$t = preg_replace('/^### (.*)$/m', '<h3>$1</h3>', $t);
|
||||
$t = preg_replace('/^## (.*)$/m', '<h2>$1</h2>', $t);
|
||||
$t = preg_replace('/^# (.*)$/m', '<h1>$1</h1>', $t);
|
||||
$t = preg_replace('/^[-*] (.*)$/m', '<li>$1</li>', $t);
|
||||
$t = preg_replace('/^\s*---\s*$/m', '<hr>', $t);
|
||||
$t = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $t);
|
||||
$t = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', '<a href="$2" target="_blank" rel="noopener">$1</a>', $t);
|
||||
$t = preg_replace('/`([^`]+)`/', '<code>$1</code>', $t);
|
||||
return $t;
|
||||
}
|
||||
function gh_api(string $url): array {
|
||||
$json = null;
|
||||
if (function_exists('curl_init')) {
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_FOLLOWLOCATION => true, CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false, CURLOPT_USERAGENT => 'pmm-site']);
|
||||
$r = curl_exec($ch); curl_close($ch);
|
||||
if (is_string($r) && $r !== '') $json = $r;
|
||||
}
|
||||
if ($json === null) $json = (string)@shell_exec('curl -s --max-time 15 ' . escapeshellarg($url));
|
||||
$d = json_decode((string)$json, true);
|
||||
return is_array($d) ? $d : [];
|
||||
}
|
||||
/* Prefer a local copy of a release asset on this server; fall back to GitHub. */
|
||||
/* Prefer a local copy of a release asset (web/releases/<tag>/, web/downloads/,
|
||||
* web/mirror/, web root) on this server; fall back to GitHub. */
|
||||
function local_asset(string $name, string $tag = ''): ?string {
|
||||
$cands = [];
|
||||
if ($tag !== '') $cands[] = __DIR__ . '/releases/' . $tag . '/' . $name;
|
||||
$cands = array_merge($cands, [__DIR__ . '/downloads/' . $name, __DIR__ . '/mirror/packages/pmm/' . $name, __DIR__ . '/' . $name]);
|
||||
$base = realpath(__DIR__);
|
||||
foreach ($cands as $p) {
|
||||
if (@is_file($p)) {
|
||||
$rp = realpath($p);
|
||||
if (strpos($rp, $base) !== 0) continue;
|
||||
return '/' . str_replace('\\', '/', substr(str_replace('\\', '/', $rp), strlen($base) + 1));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function render_releases(): void {
|
||||
$rels = gh_api('https://api.github.com/repos/JGZYES/ParlzPackageManger/releases?per_page=30');
|
||||
if (!$rels) { echo '<div class="tips">无法获取 Releases(网络或 API 受限)。</div>'; return; }
|
||||
/* left: tag list (like GitHub's folded releases) */
|
||||
echo '<div class="rel-layout"><aside class="rel-tags"><div class="rel-tags-h">Releases</div><ul>';
|
||||
$i = 0;
|
||||
foreach ($rels as $r) {
|
||||
$t = $r['tag_name'] ?? '?';
|
||||
echo '<li' . ($i === 0 ? ' class="on"' : '') . '><a href="#rel-' . esc($t) . '">' . esc($t) . '</a></li>';
|
||||
$i++;
|
||||
}
|
||||
echo '</ul></aside>';
|
||||
/* right: release entries */
|
||||
echo '<div class="rel-main">';
|
||||
$i = 0;
|
||||
foreach ($rels as $r):
|
||||
$tag = $r['tag_name'] ?? '?'; $name = $r['name'] ?? $tag;
|
||||
$date = $r['published_at'] ?? ($r['created_at'] ?? '');
|
||||
$date = substr(str_replace('T', ' ', (string)$date), 0, 10);
|
||||
$prere = !empty($r['prerelease']);
|
||||
$assets = $r['assets'] ?? [];
|
||||
$body = (string)($r['body'] ?? '');
|
||||
?>
|
||||
<div class="rel-entry" id="rel-<?php echo esc($tag); ?>"<?php echo $i === 0 ? ' data-latest="1"' : ''; ?>>
|
||||
<div class="rel-card">
|
||||
<div class="rel-head">
|
||||
<h2 class="rel-version"><span class="rel-ico">⟱</span> <?php echo esc($name); ?></h2>
|
||||
<div class="rel-badges">
|
||||
<?php if ($i === 0): ?><span class="rel-latest">Latest</span><?php endif; ?>
|
||||
<?php if ($prere): ?><span class="rel-latest prere">Pre-release</span><?php endif; ?>
|
||||
<span class="rel-date"><?php echo esc($date); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($body !== ''): ?>
|
||||
<div class="rel-body mdbody"><?php echo md($body); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($assets): ?>
|
||||
<?php
|
||||
$tot = 0; foreach ($assets as $a) $tot += (int)($a['size'] ?? 0);
|
||||
$cnt = count($assets);
|
||||
?>
|
||||
<div class="rel-assets">
|
||||
<button class="rel-assets-h" type="button" data-toggle="assets" aria-expanded="false">
|
||||
<span class="rel-chev">▸</span> Assets · 下载
|
||||
<span class="rel-count"><?php echo $cnt; ?> files · <?php echo fmt_size($tot); ?></span>
|
||||
</button>
|
||||
<div class="rel-assets-list" hidden>
|
||||
<?php foreach ($assets as $a):
|
||||
$local = local_asset((string)($a['name'] ?? ''), $tag);
|
||||
$href = $local ?: ($a['browser_download_url'] ?? '#');
|
||||
$sz = isset($a['size']) ? fmt_size((int)$a['size']) : '';
|
||||
?>
|
||||
<div class="rel-asset-row">
|
||||
<a class="rel-asset<?php echo $local ? ' local' : ''; ?>"
|
||||
href="<?php echo esc($href); ?>" title="<?php echo esc($href); ?>"
|
||||
<?php echo $local ? ' download' : ' target="_blank" rel="noopener"'; ?>>
|
||||
<span class="rel-a-ico">📄</span> <?php echo esc($a['name'] ?? 'asset'); ?>
|
||||
</a>
|
||||
<span class="rel-size"><?php echo esc($sz); ?></span>
|
||||
<a class="rel-dl" href="<?php echo esc($href); ?>" <?php echo $local ? ' download' : ' target="_blank" rel="noopener"'; ?>>下载</a>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<p class="rel-tagline">标签 <code><?php echo esc($tag); ?></code></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php $i++; endforeach;
|
||||
echo '</div></div>';
|
||||
}
|
||||
|
||||
$view = $_GET['view'] ?? '';
|
||||
$branch = $repo_ok ? sh("git -C " . escapeshellarg($REPO_DIR) . " rev-parse --abbrev-ref HEAD 2>/dev/null") : '';
|
||||
$last = $repo_ok ? sh('git -C ' . escapeshellarg($REPO_DIR) . " log -1 --format=\"%H|%s|%an|%ad\" --date=short 2>/dev/null") : '';
|
||||
[$lhash, $lsubj, $luser, $ldate] = array_pad(explode('|', $last, 4), 4, '');
|
||||
$lhash7 = substr($lhash, 0, 7);
|
||||
|
||||
pmm_header('source', '源码浏览');
|
||||
?>
|
||||
|
||||
<header class="page-hero"><div class="wrap">
|
||||
<div class="page-kicker">SOURCE</div>
|
||||
<h1>源码 <span class="accent">浏览</span></h1>
|
||||
<div class="src-clone-note">
|
||||
<?php echo $repo_ok
|
||||
? '本地仓库 <code>JGZYES/ParlzPackageManager</code> · 分支 <code>' . esc($branch) . '</code> · ' . esc($REPO_DIR)
|
||||
: '提示:本地依赖仓库尚未初始化,请先 <code>git clone</code> 或点击下方“初始化/更新”。'; ?>
|
||||
</div>
|
||||
</div></header>
|
||||
|
||||
<section class="section" style="padding-top:18px">
|
||||
<div class="wrap">
|
||||
|
||||
<?php if (!$repo_ok): ?>
|
||||
<div class="gh-card">
|
||||
<div class="gh-repo">初始化本地源码仓库</div>
|
||||
<p class="src-clone-note">页面将 PMM 仓库副本存入 <code><?php echo esc($REPO_DIR); ?></code>。若 PHP 无写权限(web 根归 root),先
|
||||
<code>chmod -R 775 <?php echo esc($REPO_DIR); ?></code> 或其父目录;或用环境变量 <code>PMM_SRC_DIR</code> 指定持久目录。初始化命令:</p>
|
||||
<pre class="gh-code"><code>git clone --depth 1 <?php echo esc($REPO_URL); ?> <?php echo esc($REPO_DIR); ?></code></pre>
|
||||
<div style="margin-top:12px">
|
||||
<button class="btn btn-sm" id="git-now" type="button">在线初始化 / 更新</button>
|
||||
<span id="git-msg" class="src-clone-note" style="margin-left:10px"></span>
|
||||
</div>
|
||||
</div>
|
||||
<?php else:
|
||||
$rel = $_GET['path'] ?? '';
|
||||
$rel = str_replace(['../', '..\\', "\0"], '', $rel);
|
||||
$rel = ltrim($rel, '/');
|
||||
$abs = repo_path($rel);
|
||||
if (!$abs) { $abs = realpath($REPO_DIR); $rel = ''; }
|
||||
$is_file = is_file($abs);
|
||||
$parts = $rel === '' ? [] : explode('/', $rel);
|
||||
?>
|
||||
<div class="gh-card">
|
||||
<div class="gh-top">
|
||||
<a class="gh-repo" href="<?php echo $REPO_URL; ?>" target="_blank" rel="noopener">JGZYES / ParlzPackageManager</a>
|
||||
<div class="gh-actions">
|
||||
<a class="btn btn-sm" href="git.php">Code</a>
|
||||
<a class="btn btn-sm" href="git.php?view=releases">Releases</a>
|
||||
<span class="gh-branch">▸ <?php echo esc($branch ?: 'main'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gh-commit">
|
||||
<span class="gh-hash" title="<?php echo esc($lhash); ?>"><?php echo esc($lhash7); ?></span>
|
||||
<span class="gh-subj"><?php echo esc($lsubj); ?></span>
|
||||
<span class="gh-meta"><?php echo esc($luser); ?> · <?php echo esc($ldate); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($view === 'releases'): ?>
|
||||
<style>
|
||||
html{scroll-padding-top:88px}
|
||||
.rel-entry{scroll-margin-top:88px}
|
||||
.rel-layout{display:grid;grid-template-columns:220px 1fr;gap:26px;align-items:start}
|
||||
@media(max-width:840px){.rel-layout{grid-template-columns:1fr}}
|
||||
.rel-tags{position:sticky;top:70px;border-right:1px solid var(--line);padding-right:12px}
|
||||
.rel-tags-h{font-size:11px;letter-spacing:1.5px;text-transform:uppercase;color:var(--dim);padding:8px 4px;font-weight:700}
|
||||
.rel-tags ul{list-style:none;margin:0;padding:0;display:grid;gap:2px}
|
||||
.rel-tags li a{display:block;padding:6px 10px;border-radius:6px;font-family:ui-monospace,monospace;font-size:12.5px;color:var(--muted);text-decoration:none}
|
||||
.rel-tags li a:hover{background:var(--panel2);color:#fff}
|
||||
.rel-tags li.on a{background:var(--panel);color:#fff;font-weight:600}
|
||||
.rel-main{display:grid;gap:24px;min-width:0}
|
||||
.rel-entry{display:grid;gap:14px}
|
||||
.rel-card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:20px 22px}
|
||||
.rel-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:10px}
|
||||
.rel-version{font-weight:800;font-size:19px;color:#fff;margin:0;display:flex;align-items:center;gap:9px;letter-spacing:-.3px}
|
||||
.rel-ico{color:var(--dim);font-size:16px}
|
||||
.rel-badges{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
||||
.rel-latest{font-size:10.5px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:#111;background:#fff;border-radius:999px;padding:2px 9px}
|
||||
.rel-latest.prere{color:#fff;background:transparent;border:1px solid var(--line);font-weight:600}
|
||||
.rel-date{font-size:12.5px;color:var(--dim);font-family:ui-monospace,monospace}
|
||||
.rel-body{border-top:1px solid var(--line);padding-top:14px;margin-top:4px}
|
||||
.rel-tagline{font-size:12px;color:var(--dim);margin-top:12px}
|
||||
.rel-assets{border-top:1px solid var(--line);margin-top:16px;padding-top:14px}
|
||||
.rel-assets-h{display:flex;align-items:center;gap:9px;background:transparent;border:0;color:var(--text);cursor:pointer;font-size:12.5px;letter-spacing:.4px;text-transform:uppercase;font-weight:700;padding:4px 0;width:100%;text-align:left}
|
||||
.rel-assets-h:hover{color:#fff}
|
||||
.rel-chev{transition:transform .18s;font-size:11px;color:var(--dim)}
|
||||
.rel-assets.open .rel-chev{transform:rotate(90deg)}
|
||||
.rel-count{margin-left:auto;font-size:11.5px;color:var(--dim);text-transform:none;letter-spacing:0;font-weight:500}
|
||||
.rel-assets-list{display:grid;gap:8px}
|
||||
.rel-assets-list[hidden]{display:none}
|
||||
.rel-assets.open .rel-assets-list{margin-top:12px}
|
||||
.rel-asset-row{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid var(--line);border-radius:8px;background:#0c1017}
|
||||
.rel-asset{display:inline-flex;align-items:center;gap:7px;color:var(--text);font-family:ui-monospace,monospace;font-size:12.5px;text-decoration:none;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.rel-asset:hover{color:#fff;text-decoration:underline}
|
||||
.rel-asset .rel-a-ico{color:var(--dim)}
|
||||
.rel-asset.local{color:#fff}
|
||||
.rel-size{font-size:11.5px;color:var(--dim);font-family:ui-monospace,monospace;white-space:nowrap}
|
||||
.rel-dl{margin-left:auto;font-size:12px;color:var(--muted);border:1px solid var(--line);border-radius:6px;padding:3px 10px;text-decoration:none;white-space:nowrap}
|
||||
.rel-dl:hover{color:#fff;border-color:#fff}
|
||||
</style>
|
||||
<h2 class="section-title" style="text-align:left;font-size:22px;margin:6px 0 20px">Releases</h2>
|
||||
<?php render_releases(); ?>
|
||||
<?php else: ?>
|
||||
<div class="gh-path">
|
||||
<a href="git.php"><code>source</code></a>
|
||||
<?php $acc=''; foreach ($parts as $p) { $acc = $acc==='' ? $p : $acc.'/'.$p;
|
||||
echo ' / <a href="git.php?path=' . urlencode($acc) . '">' . esc($p) . '</a>'; } ?>
|
||||
</div>
|
||||
|
||||
<?php if ($is_file): ?>
|
||||
<div class="gh-filehead"><span class="gh-filelink"><?php echo esc(end($parts)); ?></span>
|
||||
<span class="gh-meta"><?php echo fmt_size((int)filesize($abs)); ?> bytes</span></div>
|
||||
<pre class="gh-code"><code><?php echo esc(substr((string)@file_get_contents($abs), 0, 400000)); ?></code></pre>
|
||||
<?php else:
|
||||
$dirs = []; $files = [];
|
||||
foreach (scandir($abs) as $e) {
|
||||
if ($e === '.' || $e === '..' || $e === '.git') continue;
|
||||
$p = $abs . '/' . $e;
|
||||
if (is_dir($p)) $dirs[] = $e; else $files[] = $e;
|
||||
}
|
||||
sort($dirs); sort($files);
|
||||
$readme = '';
|
||||
foreach (['README.md', 'README.MD', 'readme.md'] as $r) if (is_file($abs . '/' . $r)) { $readme = $abs . '/' . $r; break; }
|
||||
?>
|
||||
<table class="gh-list">
|
||||
<thead><tr><th>名称</th><th>提交</th><th>时间</th><th>大小</th></tr></thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$rows = array_merge(array_map(fn($d) => [true, $d], $dirs), array_map(fn($f) => [false, $f], $files));
|
||||
foreach ($rows as [$isD, $name]):
|
||||
$sub = $rel === '' ? $name : "$rel/$name";
|
||||
$href = 'git.php?path=' . urlencode($sub);
|
||||
$commit = sh('git -C ' . escapeshellarg($REPO_DIR) . " log -1 --format=\"%h|%an|%ad\" --date=short -- " . escapeshellarg($sub) . ' 2>/dev/null');
|
||||
[$ch, $ca, $cd] = array_pad(explode('|', $commit, 3), 3, '');
|
||||
?>
|
||||
<tr>
|
||||
<td class="gh-name"><a class="<?php echo $isD ? 'gh-dir' : 'gh-file'; ?>" href="<?php echo $href; ?>">
|
||||
<span class="gh-ico"><?php echo $isD ? '📁' : '📄'; ?></span> <?php echo esc($name); ?></a></td>
|
||||
<td class="gh-c"><code><?php echo esc($ch); ?></code> <?php echo esc($ca); ?></td>
|
||||
<td class="gh-c"><?php echo esc($cd); ?></td>
|
||||
<td class="gh-c"><?php echo $isD ? '—' : fmt_size((int)@filesize($abs . '/' . $name)); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php if ($readme): ?>
|
||||
<div class="gh-readme"><div class="gh-readme-t">README</div><div class="mdbody"><?php echo md((string)@file_get_contents($readme)); ?></div></div>
|
||||
<?php endif; ?>
|
||||
<?php endif; endif; endif; ?>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
var btn = document.getElementById("git-now");
|
||||
if (btn) btn.addEventListener("click", function(){
|
||||
var msg = document.getElementById("git-msg");
|
||||
msg.textContent = "正在初始化…";
|
||||
fetch("git.php?action=init", { cache: "no-store" })
|
||||
.then(function(r){ return r.text(); })
|
||||
.then(function(t){ msg.textContent = t; if (t.indexOf("OK") === 0) setTimeout(function(){ location.reload(); }, 1200); })
|
||||
.catch(function(){ msg.textContent = "失败"; });
|
||||
});
|
||||
|
||||
/* Releases: Assets collapsible (GitHub-like) — click header to expand/collapse */
|
||||
document.querySelectorAll(".rel-assets-h").forEach(function(h){
|
||||
h.addEventListener("click", function(){
|
||||
var box = h.closest(".rel-assets");
|
||||
var list = box.querySelector(".rel-assets-list");
|
||||
var open = box.classList.toggle("open");
|
||||
list.hidden = !open;
|
||||
h.setAttribute("aria-expanded", open ? "true" : "false");
|
||||
});
|
||||
});
|
||||
|
||||
/* Releases: clicking a left tag moves the active (on) highlight there */
|
||||
var tags = document.querySelectorAll(".rel-tags li a");
|
||||
if (tags.length) {
|
||||
tags.forEach(function(a){
|
||||
a.addEventListener("click", function(){
|
||||
document.querySelectorAll(".rel-tags li").forEach(function(li){ li.classList.remove("on"); });
|
||||
a.parentElement.classList.add("on");
|
||||
});
|
||||
});
|
||||
/* keep the left highlight in sync with the release in view */
|
||||
var entries = document.querySelectorAll(".rel-entry");
|
||||
if ("IntersectionObserver" in window) {
|
||||
var io = new IntersectionObserver(function(en){
|
||||
en.forEach(function(e){
|
||||
if (e.isIntersecting) {
|
||||
var id = e.target.id.replace("rel-", "");
|
||||
document.querySelectorAll(".rel-tags li").forEach(function(li){
|
||||
li.classList.toggle("on", li.querySelector("a").textContent.trim() === id);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, { rootMargin: "-80px 0px -60% 0px" });
|
||||
entries.forEach(function(en){ io.observe(en); });
|
||||
}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<?php pmm_footer(); ?>
|
||||
+12
-21
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
/* PMM home — hero + rotating dotted Earth + entry cards to the 4 pages. */
|
||||
/* PMM home — brand hero + rotating Earth + live mirror status. Content lives
|
||||
* in the docs site (web/docs). */
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
|
||||
@@ -60,8 +61,8 @@ pmm_header('home', '跨平台 C 语言包管理器');
|
||||
<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.php">快速安装</a>
|
||||
<a class="btn btn-ghost" href="download.php">下载 v<?php echo PMM_VERSION; ?> ↓</a>
|
||||
<a class="btn btn-primary" href="docs/index.php">文档 · 安装</a>
|
||||
<a class="btn btn-ghost" href="docs/index.php?page=download">下载 v<?php echo PMM_VERSION; ?> ↓</a>
|
||||
</div>
|
||||
<div class="hero-note"><span class="dot ok"></span> 双镜像 · 深圳 / 香港 实时在线</div>
|
||||
</div>
|
||||
@@ -75,30 +76,20 @@ pmm_header('home', '跨平台 C 语言包管理器');
|
||||
<section class="section entry-section">
|
||||
<div class="wrap">
|
||||
<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 class="entry-card" href="docs/index.php">
|
||||
<span class="entry-icon">▸</span>
|
||||
<span class="entry-title">文档</span>
|
||||
<span class="entry-desc">特性、安装、下载、CLI、镜像、翻译</span>
|
||||
</a>
|
||||
<a class="entry-card" href="install.php">
|
||||
<a class="entry-card" href="docs/index.php?page=install">
|
||||
<span class="entry-icon">▸</span>
|
||||
<span class="entry-title">安装</span>
|
||||
<span class="entry-desc">三条命令装好 Windows / Linux / macOS 客户端</span>
|
||||
<span class="entry-desc">Windows / Linux / macOS 一键安装</span>
|
||||
</a>
|
||||
<a class="entry-card" href="download.php">
|
||||
<a class="entry-card" href="docs/index.php?page=download">
|
||||
<span class="entry-icon">↓</span>
|
||||
<span class="entry-title">下载</span>
|
||||
<span class="entry-desc">zip / deb / install.sh / .pdm 全部安装产物</span>
|
||||
</a>
|
||||
<a class="entry-card" href="servers.php">
|
||||
<span class="entry-icon">◉</span>
|
||||
<span class="entry-title">服务状态</span>
|
||||
<span class="entry-desc">深圳 / 香港镜像的实时探测与详细信息</span>
|
||||
</a>
|
||||
<a class="entry-card" href="git.php">
|
||||
<span class="entry-icon"></></span>
|
||||
<span class="entry-title">源码</span>
|
||||
<span class="entry-desc">GitHub 风格浏览 PMM 源码(本地仓库副本)</span>
|
||||
<span class="entry-desc">zip / deb / rpm / install.sh / .pdm</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
<?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/releases/v<?php echo PMM_VERSION; ?>/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/releases/v<?php echo PMM_VERSION; ?>/pmm_<?php echo PMM_VERSION; ?>_amd64.deb
|
||||
sudo apt install ./pmm.deb
|
||||
|
||||
# 方式三:.rpm 安装
|
||||
curl -L -o pmm.rpm https://pmm.parlz.com/releases/v<?php echo PMM_VERSION; ?>/pmm-<?php echo PMM_VERSION; ?>.x86_64.rpm
|
||||
sudo rpm -Uvh pmm.rpm
|
||||
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(); ?>
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ParlzPackageManager (PMM) — Linux/macOS one-line installer.
|
||||
#
|
||||
# 用法:
|
||||
# curl -sSL -o install.sh https://pmm.parlz.com/install.sh && bash install.sh
|
||||
# 或:curl -sSL https://pmm.parlz.com/install.sh | bash
|
||||
#
|
||||
# 下载官方 release 二进制并安装到 /usr/local/bin/pmm(root)或 ~/.local/bin/pmm。
|
||||
set -e
|
||||
VER="0.3.6"
|
||||
REPO="JGZYES/ParlzPackageManger"
|
||||
|
||||
# ---- 检测平台 ----
|
||||
OS="$(uname -s)"; ARCH="$(uname -m)"
|
||||
case "$OS" in
|
||||
Linux) os=linux ;;
|
||||
Darwin) os=macos ;;
|
||||
*) echo "pmm: 不支持的平台 $OS"; exit 1 ;;
|
||||
esac
|
||||
case "$ARCH" in
|
||||
x86_64|amd64) arch=amd64 ;;
|
||||
aarch64|arm64) arch=arm64 ;;
|
||||
*) echo "pmm: 不支持的架构 $ARCH"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# ---- 下载 pmm(release 资产名为 pmm)----
|
||||
URL="https://github.com/$REPO/releases/download/v$VER/pmm"
|
||||
if [ "$arch" != "amd64" ]; then
|
||||
echo "pmm: 注意:官方发布目前只提供 amd64 资产,$os/$arch 可能 404"
|
||||
fi
|
||||
echo "pmm: 下载 v$VER ($os/$arch) ..."
|
||||
TMP="$(mktemp)"
|
||||
# --progress-bar shows a live download progress line (tty only)
|
||||
if ! curl -fL --progress-bar --max-time 300 -o "$TMP" "$URL"; then
|
||||
rm -f "$TMP"; echo "pmm: 下载失败($URL)"; exit 1
|
||||
fi
|
||||
echo ""
|
||||
chmod +x "$TMP"
|
||||
|
||||
# ---- 安装 ----
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
DEST="/usr/local/bin/pmm"
|
||||
else
|
||||
DEST="${HOME}/.local/bin/pmm"
|
||||
fi
|
||||
mkdir -p "$(dirname "$DEST")"
|
||||
cp "$TMP" "$DEST" && chmod +x "$DEST" && rm -f "$TMP"
|
||||
|
||||
# ---- PATH 提示 ----
|
||||
case ":$PATH:" in
|
||||
*":$(dirname "$DEST"):"*) ;;
|
||||
*) echo "pmm: 请将 $(dirname "$DEST") 加入 PATH(或重新登录 shell)" ;;
|
||||
esac
|
||||
|
||||
echo "pmm: 安装完成 -> $DEST"
|
||||
"$DEST" -v 2>/dev/null || true
|
||||
+1
-1
@@ -89,7 +89,7 @@ if ($dirRel !== '') {
|
||||
}
|
||||
foreach ($dirs as $d) {
|
||||
$href = '?dir=' . rawurlencode($webRel === '' ? $d : $webRel . '/' . $d);
|
||||
echo "<li>" . ic('folder') . "<a href='${href}'>" . en($d) . "/</a></li>";
|
||||
echo "<li>" . ic('folder') . "<a href='{$href}'>" . en($d) . "/</a></li>";
|
||||
}
|
||||
foreach ($files as $f) {
|
||||
$href = url_enc_path($webRel, $f);
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
<?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(); ?>
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
/* Source browsing now lives in git.php (local stored repo copy). */
|
||||
header('Location: git.php'); exit;
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
<?php
|
||||
/* 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' => '深圳', 'region' => 'CN · 深圳', 'host' => 'sz.pmm.parlz.com'],
|
||||
['name' => '香港', 'region' => 'HK · 香港', 'host' => 'pmm.parlz.com'],
|
||||
];
|
||||
|
||||
/* Probe one URL: returns [code, ms, server, ctype].
|
||||
* Prefers the PHP curl extension (reliable CURLINFO_TOTAL_TIME for ms, and works
|
||||
* where shell_exec/curl-binary are unavailable); falls back to shell curl. */
|
||||
function probe_url(string $url): array {
|
||||
$code = 0; $ms = 0; $server = ''; $ctype = '';
|
||||
|
||||
/* 1) PHP curl extension */
|
||||
if (function_exists('curl_init')) {
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_NOBODY => true,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 3,
|
||||
CURLOPT_TIMEOUT => 8,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_USERAGENT => 'pmm-status/1.0',
|
||||
]);
|
||||
$resp = curl_exec($ch);
|
||||
$code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$ms = (int)round(((float)curl_getinfo($ch, CURLINFO_TOTAL_TIME)) * 1000.0);
|
||||
$ctype = (string)curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
if (preg_match('/^Server:\s*(.+)$/mi', (string)$resp, $m)) $server = trim($m[1]);
|
||||
curl_close($ch);
|
||||
if ($code > 0) return [$code, $ms, $server, $ctype];
|
||||
}
|
||||
|
||||
/* 2) shell curl (works where the binary exists; parses -D - headers + -w marker) */
|
||||
$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);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
$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'],
|
||||
'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);
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
|
||||
/* Language packs under web/mirror/lang/. */
|
||||
$langDir = __DIR__ . '/mirror/lang';
|
||||
$packs = [];
|
||||
if (is_dir($langDir)) {
|
||||
foreach (glob($langDir . '/*.pjson') as $f) {
|
||||
$locale = basename($f, '.pjson');
|
||||
$keys = 0; $raw = '';
|
||||
$data = json_decode((string)file_get_contents($f), true);
|
||||
if (is_array($data)) {
|
||||
$keys = count($data);
|
||||
ksort($data);
|
||||
$raw = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
$packs[] = [
|
||||
'locale' => $locale,
|
||||
'keys' => $keys,
|
||||
'file' => 'mirror/lang/' . basename($f),
|
||||
'raw' => $raw,
|
||||
];
|
||||
}
|
||||
}
|
||||
usort($packs, function ($a, $b) { return strcmp($a['locale'], $b['locale']); });
|
||||
|
||||
$mail = 'luoriguodu@qq.com';
|
||||
$issues = 'https://github.com/JGZYES/ParlzPackageManger/issues/new';
|
||||
|
||||
pmm_header('translate', '翻译 · Translate');
|
||||
pmm_page_open('TRANSLATE', '翻译 / Language');
|
||||
?>
|
||||
<section class="section">
|
||||
<div class="wrap">
|
||||
<p class="section-sub">PMM 的界面文案由 <code>.pjson</code> 语言包驱动。每门语言一个包;想新增语言或修订译文,请
|
||||
<a href="mailto:<?php echo $mail; ?>?subject=<?php echo rawurlencode('[PMM 翻译] 语言包建议'); ?>&body=<?php echo rawurlencode('我想添加/修订语言包:'); ?>">发邮件</a>
|
||||
或在 <a href="<?php echo $issues; ?>" target="_blank" rel="noopener">GitHub 提 Issue</a>。</p>
|
||||
|
||||
<div class="dl-grid" style="grid-template-columns:repeat(auto-fill,minmax(260px,1fr));">
|
||||
<?php if (!$packs): ?>
|
||||
<div class="dl-card"><span class="dl-os">—</span><span class="dl-file">暂无语言包</span></div>
|
||||
<?php else: foreach ($packs as $p): ?>
|
||||
<div class="dl-card">
|
||||
<span class="dl-os"><?php echo htmlspecialchars($p['locale']); ?></span>
|
||||
<span class="dl-file"><?php echo (int)$p['keys']; ?> 键</span>
|
||||
|
||||
<details class="lang-view">
|
||||
<summary class="btn btn-sm" style="display:inline-block; margin:8px 0 6px;">查看</summary>
|
||||
<pre class="code-box" style="max-height:220px; overflow:auto; white-space:pre-wrap; word-break:break-all;"><?php echo htmlspecialchars($p['raw']); ?></pre>
|
||||
</details>
|
||||
|
||||
<div style="display:flex; gap:8px; flex-wrap:wrap;">
|
||||
<a class="btn btn-sm" href="<?php echo htmlspecialchars($p['file']); ?>" download>下载 .pjson ↓</a>
|
||||
<a class="btn btn-sm" href="mailto:<?php echo $mail; ?>?subject=<?php echo rawurlencode('[PMM 翻译] 修订 ' . $p['locale']); ?>&body=<?php echo rawurlencode('针对 ' . $p['locale'] . ' 语言包的建议:'); ?>">贡献修订</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; endif; ?>
|
||||
</div>
|
||||
|
||||
<h2 class="section-title" style="margin-top:56px">如何贡献一门语言</h2>
|
||||
<ol class="tips" style="line-height:1.9;">
|
||||
<li>打开任一语言(如 <code>zh-CN</code>)卡片 → <b>下载 .pjson</b>。</li>
|
||||
<li>把 <code>zh-CN</code> 的键名保留、<b>只改值</b>,另存为你的语言,如 <code>ja-JP.pjson</code>、<code>zh-TW.pjson</code>。</li>
|
||||
<li>将文件作为附件发到 <a href="mailto:<?php echo $mail; ?>"><?php echo $mail; ?></a>,或在 <a href="<?php echo $issues; ?>" target="_blank" rel="noopener">GitHub 提 Issue</a> 粘贴内容。</li>
|
||||
<li>通过后即进入镜像 <code>web/mirror/lang/</code>,用户用 <code>pmm setting lang <locale></code> 安装。</li>
|
||||
</ol>
|
||||
|
||||
<p>安装语言包:</p>
|
||||
<pre class="code-box"><code>pmm setting lang ja-JP
|
||||
pmm setting lang -l</code></pre>
|
||||
</div>
|
||||
</section>
|
||||
<?php pmm_footer(); ?>
|
||||
在新工单中引用
屏蔽一个用户