Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion site/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,25 @@ async function pick(f) {

/* -------------------------------- preview -------------------------------- */

// The deb's own Maintainer field is what dpkg-scanpackages publishes into the
// APT index. When "hide my email" is checked (default) we mask the address in
// the preview so the developer isn't surprised to see it; the published record
// (release manifest) is separately kept to a noreply form by the Worker.
function maintainerDisplay(maintainer) {
const m = (maintainer || "").trim();
if (!m) return t("preview.maintainerMissing");
if (!$("hide-email").checked) return m;
const name = m.split("<")[0].trim();
const masked = t("preview.emailHiddenTag");
return name ? `${name} ${masked}` : masked;
}

// Re-mask the preview when the checkbox is toggled (only meaningful once a deb
// has been parsed).
$("hide-email").addEventListener("change", () => {
if (parsed && file) $("p-maint").textContent = maintainerDisplay(parsed.control.Maintainer);
});

async function renderPreview() {
const c = parsed.control;
$("drop-text").innerHTML = t("upload.chosen", { name: file.name, size: (file.size / 1048576).toFixed(1) });
Expand All @@ -282,7 +301,7 @@ async function renderPreview() {
$("p-version").textContent = c.Version || "?";
$("p-arch").textContent = c.Architecture || "?";
$("p-size").textContent = `${(parsed.totalInstalledSize / 1048576).toFixed(1)} MB`;
$("p-maint").textContent = c.Maintainer || t("preview.maintainerMissing");
$("p-maint").textContent = maintainerDisplay(c.Maintainer);

$("s-title").placeholder = (parsed.desktop && parsed.desktop.Name) || c.Package || t("preview.titlePlaceholder");

Expand Down Expand Up @@ -461,6 +480,9 @@ $("submit-btn").addEventListener("click", async () => {
body.append("arch", parsed.control.Architecture);
const repo = $("source-repo").value.trim();
if (repo) body.append("source_repo", repo);
// Privacy: when checked (default) the Worker records a noreply address; when
// unchecked it publishes the developer's verified GitHub email instead.
body.append("hide_email", $("hide-email").checked ? "true" : "false");

// Optional store metadata (empty strings + no images ⇒ Worker treats it as
// "not supplied" and falls back to source_repo/deb-derived metadata).
Expand Down
2 changes: 2 additions & 0 deletions site/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"version": "Version",
"installSize": "Installed size",
"maintainerMissing": "(missing)",
"emailHiddenTag": "<email hidden>",
"reportTitle": "Preliminary check report",
"filesSummary": "File list ({count} files)",
"scripts": "Maintainer script contents",
Expand Down Expand Up @@ -52,6 +53,7 @@
"screenshotsLabel": "Screenshots (320×170, auto-cropped and resized; up to 6)",
"addShot": "+ Add screenshot",
"shotLimit": "Reached the 6-image limit",
"hideEmail": "Don't publicly show my email address (kept only for identity records; hidden by default — uncheck to publish your GitHub email)",
"shotAlt": "Screenshot {n}",
"shotProcessFailed": "Screenshot processing failed: {msg}",
"iconProcessFailed": "Icon processing failed: {msg}",
Expand Down
2 changes: 2 additions & 0 deletions site/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"version": "バージョン",
"installSize": "インストール容量",
"maintainerMissing": "(未設定)",
"emailHiddenTag": "<メール非表示>",
"reportTitle": "事前チェック結果",
"filesSummary": "ファイル一覧({count} 個)",
"scripts": "maintainer スクリプトの内容",
Expand Down Expand Up @@ -52,6 +53,7 @@
"screenshotsLabel": "スクリーンショット(320×170、選択後に自動でトリミング・リサイズ・最大 6 枚)",
"addShot": "+ スクリーンショットを追加",
"shotLimit": "6 枚の上限に達しました",
"hideEmail": "メールアドレスを公開しない(本人確認の記録のみに使用・既定で非表示。チェックを外すと GitHub のメールを公開します)",
"shotAlt": "スクリーンショット {n}",
"shotProcessFailed": "スクリーンショットの処理に失敗しました:{msg}",
"iconProcessFailed": "アイコンの処理に失敗しました:{msg}",
Expand Down
2 changes: 2 additions & 0 deletions site/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"version": "版本",
"installSize": "安装体积",
"maintainerMissing": "(缺失)",
"emailHiddenTag": "<邮箱已隐藏>",
"reportTitle": "初步检查报告",
"filesSummary": "文件清单({count} 个文件)",
"scripts": "maintainer 脚本内容",
Expand Down Expand Up @@ -52,6 +53,7 @@
"screenshotsLabel": "应用截图(320×170,选图后自动裁剪缩放;最多 6 张)",
"addShot": "+ 添加截图",
"shotLimit": "已达 6 张上限",
"hideEmail": "不公开显示我的邮箱地址(仅用于身份记录,默认隐藏;取消勾选则公开你的 GitHub 邮箱)",
"shotAlt": "截图 {n}",
"shotProcessFailed": "截图处理失败:{msg}",
"iconProcessFailed": "图标处理失败:{msg}",
Expand Down
8 changes: 8 additions & 0 deletions site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@
text-align: center; color: var(--dim); cursor: pointer; transition: border-color .15s;
}
.drop.hover, .drop:hover { border-color: var(--accent); color: var(--text); }
.checkbox-row { display: flex; align-items: flex-start; gap: 8px; margin-top: 18px;
color: var(--dim); font-size: 13px; cursor: pointer; line-height: 1.5; }
.checkbox-row input { width: auto; margin: 3px 0 0; flex: none; accent-color: var(--accent); }
button.primary {
margin-top: 18px; width: 100%; padding: 12px; border: 0; border-radius: 6px;
background: var(--accent); color: #04220b; font: inherit; font-weight: 700; cursor: pointer;
Expand Down Expand Up @@ -214,6 +217,11 @@ <h1 data-i18n="header.title">CardputerZero 开发者中心</h1>
<button type="button" class="ghost" id="s-shot-btn" data-i18n="form.addShot">+ 添加截图</button>
</fieldset>

<label class="checkbox-row">
<input type="checkbox" id="hide-email" checked>
<span data-i18n="form.hideEmail">不公开显示我的邮箱地址(仅用于身份记录,默认隐藏;取消勾选则公开你的 GitHub 邮箱)</span>
</label>

<button class="primary" id="submit-btn" disabled data-i18n="preview.submit">提交到 AppStore</button>
</div>
<div class="status" id="status"></div>
Expand Down
16 changes: 15 additions & 1 deletion worker/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ async function apiSubmit(request, env) {
const version = String(form.get("version") || "").trim();
const arch = String(form.get("arch") || "").trim();
const sourceRepo = String(form.get("source_repo") || "").trim();
// Privacy toggle (default: hide). When hidden we only forward the noreply
// form(s), so the packages-repo Action records a noreply address as the
// uploader email. When the developer opts in, a verified GitHub email is put
// first so it becomes the recorded contact address.
const hideEmail = form.get("hide_email") !== "false";

if (!(file && typeof file.arrayBuffer === "function")) return json(400, { error: "missing_deb_file" });
if (!/^[a-z0-9][a-z0-9.+-]+$/.test(pkg)) return json(400, { error: "bad_package_name" });
Expand Down Expand Up @@ -242,13 +247,22 @@ async function apiSubmit(request, env) {
}
if (store && store.error) return json(store.status || 400, { error: store.error, detail: store.detail });

// s.e is [noreply…, …verified]; the Action uses element [0] as the recorded
// uploader email. Hide → keep only noreply; show → put a verified email first
// (falling back to noreply when the account exposes none).
const noreply = s.e.filter((e) => e.endsWith("@users.noreply.github.com"));
const verified = s.e.filter((e) => !e.endsWith("@users.noreply.github.com"));
const emailsOut = hideEmail
? noreply
: (verified.length ? [...verified, ...noreply] : s.e);

const dispatch = await gh(env, `/repos/${env.TARGET_OWNER}/${env.TARGET_REPO}/dispatches`, {
method: "POST",
body: JSON.stringify({
event_type: "web-submission",
client_payload: {
login: s.l,
emails: s.e,
emails: emailsOut,
is_admin: s.a,
package: pkg,
version,
Expand Down
Loading