Swadhin Biswas

[email protected]
2026-04-22 02:49:04 +0600 committed NaN years ago
e98431e
commit e98431e
15 files changed
+85 -72
▼src/components/repo/RepoHeader.astro
+24-18
13
13
import { Badge } from "@/components/ui/badge";
14
14
import { RepoActions } from "@/components/repo/RepoActions";
15
15
16
+type RepoProps = {
17
+ owner: string;
18
+ name: string;
19
+ description?: string | null;
20
+ visibility?: string;
21
+ topics?: unknown[];
22
+ watchers?: number;
23
+ forks?: number;
24
+ stars?: number;
25
+ openIssueCount?: number;
26
+ openPrCount?: number;
27
+ website?: string | null;
28
+ license?: string | null;
29
+ updatedAt?: Date | string | number | null;
30
+ defaultBranch?: string;
31
+ language?: string;
32
+ id?: string;
33
+ [key: string]: any;
34
+};
35
+
16
36
interface Props {
17
- repo: {
18
- owner: string;
19
- name: string;
20
- description?: string | null;
21
- visibility: string;
22
- topics?: string[];
23
- watchers?: number;
24
- forks?: number;
25
- stars?: number;
26
- openIssueCount?: number;
27
- openPrCount?: number;
28
- website?: string | null;
29
- license?: string | null;
30
- updatedAt?: Date | string | number | null;
31
- };
37
+ repo: RepoProps;
32
38
activeTab:
33
39
| "code"
34
40
| "issues"
214
220
{repo.updatedAt && <div>Updated {timeAgo(repo.updatedAt)}</div>}
215
221
</div>
216
222
217
- <!-- Topics -->
223
+ {/* Topics */}
218
224
{
219
- repo.topics && repo.topics.length > 0 && (
225
+ repo.topics && (repo.topics as unknown[]).length > 0 && (
220
226
<div class="flex flex-wrap gap-2">
221
- {repo.topics.map((topic: string) => (
227
+ {(repo.topics as any[]).map((topic: any) => (
222
228
<Badge
223
229
variant="secondary"
224
230
className="rounded-full hover:bg-primary/20 hover:text-primary cursor-pointer transition-colors"
225
231
▼src/pages/[owner]/[repo]/actions/runs/[id].astro
+1-1
250
250
:class="autoScroll ? 'text-green-400 bg-green-400/10 border-green-400/20' : 'text-gray-500 hover:text-gray-300 border-transparent'"
251
251
class="px-2 py-1 rounded text-xs border transition-colors flex items-center gap-1.5"
252
252
>
253
- <div class={`h-1.5 w-1.5 rounded-full ${autoScroll ? 'bg-green-400 animate-pulse' : 'bg-gray-500'}`}></div>
253
+ <div class={`h-1.5 w-1.5 rounded-full bg-gray-500`}></div>
254
254
Follow
255
255
</button>
256
256
<div class="h-4 w-px bg-white/10 mx-1"></div>
257
257
▼src/pages/[owner]/[repo]/issues/[number].astro
+3-2
6
6
import { renderMarkdown } from "@/lib/markdown";
7
7
import { canReadRepo, canWriteRepo } from "@/lib/permissions";
8
8
import { and, eq } from "drizzle-orm";
9
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
9
10
10
11
const { owner: ownerName, repo: repoName, number } = Astro.params;
11
-const db = getDatabase();
12
+const db = getDatabase() as NodePgDatabase<typeof schema>;
12
13
13
14
// 1. Fetch Repo & Owner
14
15
const user = await db.query.users.findFirst({
137
138
<BaseLayout
138
139
title={`${issue.title} · Issue #${issue.number} · ${ownerName}/${repoName}`}
139
140
>
140
- <RepoHeader repo={repoForHeader} activeTab="issues" />
141
+ <RepoHeader repo={repoForHeader as any} activeTab="issues" />
141
142
142
143
<div class="container py-8">
143
144
<IssueDetail
144
145
▼src/pages/[owner]/[repo]/issues/new.astro
+1-1
57
57
---
58
58
59
59
<BaseLayout title={`New Issue · ${ownerName}/${repoName}`}>
60
- <RepoHeader repo={repoForHeader} activeTab="issues" />
60
+ <RepoHeader repo={repoForHeader as any} activeTab="issues" />
61
61
62
62
<div class="container max-w-4xl py-8">
63
63
<div class="mb-6">
64
64
▼src/pages/[owner]/[repo]/projects/index.astro
+4-3
1
1
---
2
2
import RepoLayout from "@/layouts/RepoLayout.astro";
3
3
import { getRepoAndUser } from "@/lib/auth";
4
-import { getDatabase } from "@/db";
4
+import { getDatabase, schema } from "@/db";
5
5
import { projects } from "@/db/schema/projects";
6
6
import { eq, desc } from "drizzle-orm";
7
7
import { Plus } from "lucide-react";
8
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
8
9
9
10
const { owner, repo } = Astro.params;
10
11
const repoInfo = await getRepoAndUser(Astro.request, owner!, repo!);
14
15
}
15
16
16
17
const { repository, permission, user } = repoInfo;
17
-const db = getDatabase();
18
+const db = getDatabase() as NodePgDatabase<typeof schema>;
18
19
19
20
const projectList = await db
20
21
.select()
41
42
42
43
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
43
44
{
44
- projectList.map((project) => (
45
+ projectList.map((project: any) => (
45
46
<a
46
47
href={`/${owner}/${repo}/projects/${project.id}`}
47
48
class="block p-6 bg-[#161b22] border border-gray-800 rounded-xl hover:border-gray-600 transition-colors group"
48
49
▼src/pages/[owner]/[repo]/pulls/index.astro
+5-4
4
4
import { getDatabase, schema } from "@/db";
5
5
import BaseLayout from "@/layouts/BaseLayout.astro";
6
6
import { canReadRepo } from "@/lib/permissions";
7
-import { and, desc, eq, sql, count } from "drizzle-orm";
7
+import { and, desc, eq, sql } from "drizzle-orm";
8
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
8
9
9
10
const { owner: ownerName, repo: repoName } = Astro.params;
10
-const db = getDatabase();
11
+const db = getDatabase() as NodePgDatabase<typeof schema>;
11
12
12
13
// 1. Fetch Repo & Owner
13
14
const user = await db.query.users.findFirst({
38
39
39
40
// Fetch issue count
40
41
const issueCountResult = await db
41
- .select({ count: count() })
42
+ .select({ count: sql`count` })
42
43
.from(schema.issues)
43
44
.where(
44
45
and(
113
114
114
115
<BaseLayout title={`Pull Requests · ${repo.owner}/${repo.name}`}>
115
116
<div class="container py-6">
116
- <RepoHeader repo={repo} activeTab="pulls" />
117
+ <RepoHeader repo={repo as any} activeTab="pulls" />
117
118
118
119
<div class="mt-6">
119
120
<PullRequestsList
120
121
▼src/pages/[owner]/[repo]/pulls/new.astro
+12-14
9
9
type CommitInfo,
10
10
type DiffInfo,
11
11
} from "@/lib/git";
12
-
13
-// ... (imports remain)
14
-
15
-// Fetch comparison data if branches are different
16
-let diffData: { commits: CommitInfo[]; diffs: DiffInfo[] } = {
17
- commits: [],
18
- diffs: [],
19
-};
20
12
import { canReadRepo } from "@/lib/permissions";
21
13
import { getRepoPath } from "@/lib/utils";
22
-import { and, eq, sql, count } from "drizzle-orm";
14
+import { and, eq, sql } from "drizzle-orm";
23
15
import {
24
16
ArrowLeft,
25
17
ArrowRight,
29
21
FileText,
30
22
GitCommit,
31
23
} from "lucide-react";
24
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
32
25
33
26
const { owner: ownerName, repo: repoName } = Astro.params;
34
-const db = getDatabase();
27
+const db = getDatabase() as NodePgDatabase<typeof schema>;
28
+
29
+let diffData: { commits: CommitInfo[]; diffs: DiffInfo[] } = {
30
+ commits: [],
31
+ diffs: [],
32
+};
35
33
36
34
// 1. Fetch Repo & Owner
37
35
const user = await db.query.users.findFirst({
80
78
// Fetch comparison data if branches are different
81
79
// diffData is typed above
82
80
83
-let error = "";
81
+let _error = "";
84
82
let canMerge = false;
85
83
86
84
if (baseBranch !== headBranch) {
89
87
diffData = await compareBranches(repoPath, baseBranch, headBranch);
90
88
canMerge = true; // Simplified: assume mergeable if git commands succeeded
91
89
} catch (e) {
92
- error = "Failed to compare branches.";
90
+ _error = "Failed to compare branches.";
93
91
console.error(e);
94
92
}
95
93
}
96
94
97
95
// Fetch open issue count (for header)
98
96
const issueCountResult = await db
99
- .select({ count: count() })
97
+ .select({ count: sql`count` })
100
98
.from(schema.issues)
101
99
.where(
102
100
and(
129
127
130
128
<BaseLayout title={`New Pull Request · ${repo.owner}/${repo.name}`}>
131
129
<div class="container py-6">
132
- <RepoHeader repo={repo} activeTab="pulls" />
130
+ <RepoHeader repo={repo as any} activeTab="pulls" />
133
131
134
132
<div class="mb-6">
135
133
<h1 class="text-2xl font-semibold mb-4">Compare changes</h1>
136
134
▼src/pages/[owner]/[repo]/releases/[id].astro
+3-2
3
3
import RepoHeader from "@/components/repo/RepoHeader.astro";
4
4
import { getDatabase, schema } from "@/db";
5
5
import { eq, and } from "drizzle-orm";
6
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
6
7
import { canReadRepo, canWriteRepo } from "@/lib/permissions";
7
8
import { renderMarkdown } from "@/lib/markdown";
8
9
import { Tag, Clock, Download, Edit, Trash2, ArrowLeft } from "lucide-react";
9
10
10
11
const { owner: ownerName, repo: repoName, id: releaseId } = Astro.params;
11
-const db = getDatabase();
12
+const db = getDatabase() as NodePgDatabase<typeof schema>;
12
13
13
14
const user = await db.query.users.findFirst({
14
15
where: eq(schema.users.username, ownerName!),
126
127
<Clock className="h-4 w-4" />
127
128
{formatDate(release.publishedAt || release.createdAt)}
128
129
</div>
129
- {release.downloadCount > 0 && (
130
+ {release.downloadCount != null && release.downloadCount > 0 && (
130
131
<div class="flex items-center gap-2 text-muted-foreground">
131
132
<Download className="h-4 w-4" />
132
133
{release.downloadCount} downloads
133
134
▼src/pages/[owner]/[repo]/tree/[...ref].astro
+5-4
12
12
Clock,
13
13
} from "lucide-react";
14
14
import { getDatabase, schema } from "@/db";
15
-import { eq, and, sql, count } from "drizzle-orm";
15
+import { eq, and, sql } from "drizzle-orm";
16
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
16
17
import {
17
18
listFiles,
18
19
getLastCommit,
27
28
import FileExplorer from "@/components/repo/FileExplorer.astro";
28
29
29
30
const { owner: ownerName, repo: repoName } = Astro.params;
30
-const db = getDatabase();
31
+const db = getDatabase() as NodePgDatabase<typeof schema>;
31
32
32
33
// 1. Fetch Repo & Owner
33
34
const user = await db.query.users.findFirst({
58
59
59
60
// Fetch issue count
60
61
const issueCountResult = await db
61
- .select({ count: count() })
62
+ .select({ count: sql`count` })
62
63
.from(schema.issues)
63
64
.where(
64
65
and(
313
314
314
315
<BaseLayout title={`${repo.owner}/${repo.name} - OpenCodeHub`}>
315
316
<div class="container py-6">
316
- <RepoHeader repo={repo} activeTab="code" />
317
+ <RepoHeader repo={repo as any} activeTab="code" />
317
318
318
319
<div class="grid gap-6 lg:grid-cols-4">
319
320
<!-- Main Content -->
320
321
▼src/pages/[owner]/[repo]/tree/[branch]/[...path].astro
+5-4
12
12
Clock,
13
13
} from "lucide-react";
14
14
import { getDatabase, schema } from "@/db";
15
-import { eq, and, sql, count } from "drizzle-orm";
15
+import { eq, and, sql } from "drizzle-orm";
16
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
16
17
import { listFiles, getLastCommit, getCommits, getBranches } from "@/lib/git";
17
18
import { resolveRepoPath } from "@/lib/git-storage";
18
19
import { canReadRepo } from "@/lib/permissions";
20
21
import FileExplorer from "@/components/repo/FileExplorer.astro";
21
22
22
23
const { owner: ownerName, repo: repoName, branch, path } = Astro.params;
23
-const db = getDatabase();
24
+const db = getDatabase() as NodePgDatabase<typeof schema>;
24
25
25
26
// 1. Fetch Repo & Owner
26
27
const user = await db.query.users.findFirst({
51
52
52
53
// Fetch issue count
53
54
const issueCountResult = await db
54
- .select({ count: count() })
55
+ .select({ count: sql`count` })
55
56
.from(schema.issues)
56
57
.where(
57
58
and(
179
180
180
181
<BaseLayout title={`${repo.owner}/${repo.name} - OpenCodeHub`}>
181
182
<div class="container py-6">
182
- <RepoHeader repo={repo} activeTab="code" />
183
+ <RepoHeader repo={repo as any} activeTab="code" />
183
184
184
185
<div class="grid gap-6 lg:grid-cols-4">
185
186
<!-- Main Content -->
186
187
▼src/pages/[owner]/index.astro
+5-4
1
1
---
2
2
import { getDatabase, schema } from "@/db";
3
3
import BaseLayout from "@/layouts/BaseLayout.astro";
4
-import { and, desc, eq, count } from "drizzle-orm";
4
+import { and, desc, eq, sql } from "drizzle-orm";
5
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
5
6
6
7
const { owner: username } = Astro.params;
7
-const db = getDatabase();
8
+const db = getDatabase() as NodePgDatabase<typeof schema>;
8
9
9
10
// 1. Fetch User
10
11
const user = await db.query.users.findFirst({
21
22
22
23
// 3. Fetch Follow Stats
23
24
const [followersResult] = await db
24
- .select({ count: count() })
25
+ .select({ count: sql`count` })
25
26
.from(schema.userFollowers)
26
27
.where(eq(schema.userFollowers.followeeId, user.id));
27
28
const followersCount = followersResult?.count || 0;
28
29
29
30
const [followingResult] = await db
30
- .select({ count: count() })
31
+ .select({ count: sql`count` })
31
32
.from(schema.userFollowers)
32
33
.where(eq(schema.userFollowers.followerId, user.id));
33
34
const followingCount = followingResult?.count || 0;
34
35
▼src/pages/admin/audit.astro
+6-5
1
1
---
2
2
import AdminLayout from "@/layouts/AdminLayout.astro";
3
3
import { getDatabase, schema } from "@/db";
4
-import { desc, like, eq, or, count } from "drizzle-orm";
4
+import { desc, like, eq, or, sql } from "drizzle-orm";
5
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
5
6
import { Button } from "@/components/ui/button";
6
7
import { Input } from "@/components/ui/input";
7
8
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
25
26
return Astro.redirect("/");
26
27
}
27
28
28
-const db = getDatabase();
29
+const db = getDatabase() as NodePgDatabase<typeof schema>;
29
30
const searchQuery = Astro.url.searchParams.get("q") || "";
30
31
const actionFilter = Astro.url.searchParams.get("action") || "";
31
32
const page = parseInt(Astro.url.searchParams.get("page") || "1");
69
70
},
70
71
}),
71
72
db
72
- .select({ count: count() })
73
+ .select({ count: sql`count` })
73
74
.from(schema.auditLogs)
74
75
.where(whereClause)
75
76
.limit(1),
139
140
<p class="text-2xl font-bold">
140
141
{
141
142
logs.filter(
142
- (l) =>
143
+ (l: typeof logs[number]) =>
143
144
new Date(l.createdAt).toDateString() ===
144
145
new Date().toDateString(),
145
146
).length
237
238
</div>
238
239
)}
239
240
{log.actorIp && <span>IP: {log.actorIp}</span>}
240
- <span>{timeAgo(log.createdAt)}</span>
241
+ <span>{timeAgo(log.createdAt.toISOString())}</span>
241
242
</div>
242
243
{data && (
243
244
<details class="mt-2">
244
245
▼src/pages/admin/repositories.astro
+2-2
240
240
row.dataset.archived = archived ? "false" : "true";
241
241
btn.setAttribute("title", archived ? "Archive" : "Unarchive");
242
242
} catch (error) {
243
- alert(error.message || "Failed to update repository");
243
+ alert(error instanceof Error ? error.message : "Failed to update repository");
244
244
} finally {
245
245
btn.removeAttribute("disabled");
246
246
}
269
269
270
270
row.remove();
271
271
} catch (error) {
272
- alert(error.message || "Failed to delete repository");
272
+ alert(error instanceof Error ? error.message : "Failed to delete repository");
273
273
btn.removeAttribute("disabled");
274
274
}
275
275
});
276
276
▼src/pages/admin/users.astro
+8-7
1
1
---
2
2
import AdminLayout from "@/layouts/AdminLayout.astro";
3
3
import { getDatabase, schema } from "@/db";
4
-import { desc, eq, like, or, count } from "drizzle-orm";
4
+import { desc, eq, like, or, sql } from "drizzle-orm";
5
+import type { NodePgDatabase } from "drizzle-orm/node-postgres";
5
6
import { Button } from "@/components/ui/button";
6
7
import { Input } from "@/components/ui/input";
7
8
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
22
23
return Astro.redirect("/");
23
24
}
24
25
25
-const db = getDatabase();
26
+const db = getDatabase() as NodePgDatabase<typeof schema>;
26
27
const searchQuery = Astro.url.searchParams.get("q") || "";
27
28
const page = parseInt(Astro.url.searchParams.get("page") || "1");
28
29
const limit = 20;
47
48
offset,
48
49
}),
49
50
db
50
- .select({ count: count() })
51
+ .select({ count: sql`count` })
51
52
.from(schema.users)
52
53
.where(whereCondition)
53
54
.limit(1),
59
60
// Stats
60
61
const [totalUsersResult, activeUsersResult, adminUsersResult] = await Promise.all([
61
62
db
62
- .select({ count: count() })
63
+ .select({ count: sql`count` })
63
64
.from(schema.users)
64
65
.limit(1),
65
66
db
66
- .select({ count: count() })
67
+ .select({ count: sql`count` })
67
68
.from(schema.users)
68
69
.where(eq(schema.users.isActive, true))
69
70
.limit(1),
70
71
db
71
- .select({ count: count() })
72
+ .select({ count: sql`count` })
72
73
.from(schema.users)
73
74
.where(eq(schema.users.isAdmin, true))
74
75
.limit(1),
187
188
</thead>
188
189
<tbody>
189
190
{
190
- users.map((u) => (
191
+ users.map((u: typeof users[number]) => (
191
192
<tr class="border-b hover:bg-muted/50" data-user-id={u.id}>
192
193
<td class="p-3">
193
194
<div class="flex items-center gap-3">
194
195
▼src/pages/api/stacks/user.ts
+1-1
1
1
import { getDatabase, schema } from "@/db";
2
2
import { type APIContext } from "astro";
3
3
import { and, desc, eq, inArray } from "drizzle-orm";
4
-import { success, error, unauthorized } from "@/lib/api";
4
+import { success, unauthorized } from "@/lib/api";
5
5
import { logger } from "@/lib/logger";
6
6
import { withErrorHandler } from "@/lib/errors";
7
7
8
8