e98431e
commit e98431e
15 files changed
+85 -72
Showing 15 changed files+85-72
src/components/repo/RepoHeader.astrosrc/pages/[owner]/[repo]/actions/runs/[id].astrosrc/pages/[owner]/[repo]/issues/[number].astrosrc/pages/[owner]/[repo]/issues/new.astrosrc/pages/[owner]/[repo]/projects/index.astrosrc/pages/[owner]/[repo]/pulls/index.astrosrc/pages/[owner]/[repo]/pulls/new.astrosrc/pages/[owner]/[repo]/releases/[id].astrosrc/pages/[owner]/[repo]/tree/[...ref].astrosrc/pages/[owner]/[repo]/tree/[branch]/[...path].astrosrc/pages/[owner]/index.astrosrc/pages/admin/audit.astrosrc/pages/admin/repositories.astrosrc/pages/admin/users.astrosrc/pages/api/stacks/user.ts
▼src/components/repo/RepoHeader.astro
+24-18
| @@ -13,22 +13,28 @@ import { | ||
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,11 +220,11 @@ const tabs = [ | ||
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,7 +250,7 @@ const activeJobId = activeJob?.id || ""; | ||
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,9 +6,10 @@ import BaseLayout from "@/layouts/BaseLayout.astro"; | ||
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,7 +138,7 @@ const formattedIssue = { | ||
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,7 +57,7 @@ const customFields = await db.query.customFieldDefinitions.findMany({ | ||
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,10 +1,11 @@ | ||
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,7 +15,7 @@ if (!repoInfo) { | ||
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,7 +42,7 @@ const projectList = await db | ||
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,10 +4,11 @@ import PullRequestsList from "@/components/pulls/PullRequestsList"; | ||
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,7 +39,7 @@ if (!hasAccess) { | ||
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,7 +114,7 @@ const formattedPRs = prsList.map((pr) => ({ | ||
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,17 +9,9 @@ import { | ||
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,9 +21,15 @@ import { | ||
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,7 +78,7 @@ const headBranch = | ||
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,14 +87,14 @@ 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,7 +127,7 @@ const repo = { | ||
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,12 +3,13 @@ import BaseLayout from "@/layouts/BaseLayout.astro"; | ||
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,7 +127,7 @@ const repo = { | ||
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,7 +12,8 @@ import { | ||
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,7 +28,7 @@ import RepoHeader from "@/components/repo/RepoHeader.astro"; | ||
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,7 +59,7 @@ if (!hasAccess) { | ||
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,7 +314,7 @@ const repo = { | ||
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,7 +12,8 @@ import { | ||
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,7 +21,7 @@ import RepoHeader from "@/components/repo/RepoHeader.astro"; | ||
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,7 +52,7 @@ if (!hasAccess) { | ||
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,7 +180,7 @@ try { | ||
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,10 +1,11 @@ | ||
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,13 +22,13 @@ const isOwner = currentUser?.id === user.id; | ||
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,7 +1,8 @@ | ||
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,7 +26,7 @@ if (!user?.isAdmin) { | ||
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,7 +70,7 @@ const [logs, totalResult] = await Promise.all([ | ||
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,7 +140,7 @@ function timeAgo(date: string) { | ||
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,7 +238,7 @@ function timeAgo(date: string) { | ||
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,7 +240,7 @@ const totalPages = Math.ceil(total / limit); | ||
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,7 +269,7 @@ const totalPages = Math.ceil(total / limit); | ||
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,7 +1,8 @@ | ||
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,7 +23,7 @@ if (!user?.isAdmin) { | ||
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,7 +48,7 @@ const [users, totalResult] = await Promise.all([ | ||
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,16 +60,16 @@ const totalPages = Math.ceil(total / limit); | ||
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,7 +188,7 @@ const adminUsers = Number(adminUsersResult[0]?.count) || 0; | ||
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,7 +1,7 @@ | ||
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 | |