ework-web 0.10.102 → 0.10.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-web",
3
- "version": "0.10.102",
3
+ "version": "0.10.104",
4
4
  "type": "module",
5
5
  "description": "ework-web — standalone multi-project issue tracker. Local SQLite-backed, no external API dependency. Bun + TypeScript + SSR HTML.",
6
6
  "license": "MIT",
package/src/store.ts CHANGED
@@ -95,6 +95,7 @@ export interface CommentRow {
95
95
  author_kind?: UserKind;
96
96
  author_display_name?: string | null;
97
97
  model?: string;
98
+ upstream_comment_id?: number | null;
98
99
  }
99
100
 
100
101
  export interface LabelRow {
@@ -627,6 +628,20 @@ export async function getIssueByUpstreamNumber(projectId: number, upstreamNumber
627
628
  );
628
629
  }
629
630
 
631
+ export async function linkIssueToUpstream(issueId: number, upstreamNumber: number): Promise<boolean> {
632
+ try {
633
+ const res = await getDB().run(
634
+ "UPDATE {{issues}} SET upstream_issue_number = ?, updated_at = ? WHERE id = ? AND upstream_issue_number IS NULL",
635
+ [upstreamNumber, now(), issueId]
636
+ );
637
+ return res.changes > 0;
638
+ } catch {
639
+ // Unique index (project_id, upstream_issue_number): another row already
640
+ // holds this mapping — leave it alone, the existing winner stays linked.
641
+ return false;
642
+ }
643
+ }
644
+
630
645
  export async function getCommentByUpstreamId(upstreamCommentId: number): Promise<CommentRow | null> {
631
646
  return await getDB().get<CommentRow>(
632
647
  "SELECT * FROM {{comments}} WHERE upstream_comment_id = ?",
@@ -3,7 +3,9 @@ import {
3
3
  type ProjectRow,
4
4
  type IssueRow,
5
5
  getProjectById,
6
+ getIssue,
6
7
  getIssueByUpstreamNumber,
8
+ linkIssueToUpstream,
7
9
  getCommentByUpstreamId,
8
10
  createIssue,
9
11
  ensureUser,
@@ -110,6 +112,22 @@ export class UpstreamSync {
110
112
  }
111
113
 
112
114
  private async importIssue(gi: GiteaIssue, emit: boolean): Promise<void> {
115
+ const body = gi.body ?? "";
116
+ // Issues ework-mirror created upstream reference their origin issue by
117
+ // number in the footer. Linking instead of importing prevents the echo
118
+ // loop (local → mirror → GitHub → poll → twin) while keeping comment and
119
+ // state sync flowing to the original.
120
+ const mirrored = body.includes("<!-- ework-mirror -->")
121
+ ? body.match(/Mirrored from ework issue #(\d+)/)
122
+ : null;
123
+ if (mirrored?.[1]) {
124
+ const origin = await getIssue(this.project.id, Number(mirrored[1]));
125
+ if (origin) {
126
+ await linkIssueToUpstream(origin.id, gi.number);
127
+ return;
128
+ }
129
+ }
130
+ if (body.includes("<!-- ework-mirror -->")) return;
113
131
  await ensureUser(gi.user?.login ?? "upstream", fromGithubBot(gi.user?.login) ? "bot" : "human");
114
132
  await createIssue(
115
133
  this.project.id,
@@ -82,7 +82,7 @@ function syncCardHtml(project: ProjectRow, sync: UpstreamSyncRow | null): string
82
82
  ${statusHtml}
83
83
  <div class="form-grid">
84
84
  <div><label for="s-base">上游地址(Gitea 根地址,如 http://host:3000)</label>
85
- <input id="s-base" name="base_url" type="url" placeholder="http://192.168.10.96:3300" value="${escapeAttr(baseUrl)}" required></div>
85
+ <input id="s-base" name="base_url" type="url" placeholder="http://192.168.1.100:3300" value="${escapeAttr(baseUrl)}" required></div>
86
86
  <div><label for="s-owner">上游 owner</label>
87
87
  <input id="s-owner" name="upstream_owner" value="${escapeAttr(sync?.upstream_owner ?? project.owner)}" required></div>
88
88
  <div><label for="s-repo">上游 repo</label>
package/src/webhooks.ts CHANGED
@@ -307,6 +307,9 @@ interface PayloadComment {
307
307
  updated_at: string;
308
308
  user: PayloadUser;
309
309
  author_kind?: string;
310
+ /** Set when this comment was imported from an upstream tracker — receivers
311
+ * must skip echoing it back (defense beyond the body marker). */
312
+ upstream_comment_id?: number | null;
310
313
  }
311
314
 
312
315
  interface IssueEventPayload {
@@ -467,6 +470,7 @@ function buildComment(
467
470
  body: comment.body,
468
471
  created_at: comment.created_at,
469
472
  updated_at: comment.updated_at,
473
+ upstream_comment_id: comment.upstream_comment_id ?? null,
470
474
  user: buildUser(comment.author, origin),
471
475
  };
472
476
  }