Skip to content

Commit 33104b2

Browse files
committed
feat: Add admin pages for article and team member management with new utility functions.
1 parent 4388389 commit 33104b2

5 files changed

Lines changed: 25 additions & 7 deletions

File tree

app/admin/articles/[id]/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { Checkbox } from "@/components/ui/checkbox"
1313
import { useToast } from "@/hooks/use-toast"
1414
import { Breadcrumb } from "@/components/breadcrumb"
1515
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
16-
import { formatDate } from "@/lib/utils"
16+
import { formatDate, sanitizeUrl } from "@/lib/utils"
1717
import { Loader2, AlertTriangle } from "lucide-react"
1818
import { MarkdownToolbar } from "@/components/markdown-toolbar"
1919
import { Switch } from "@/components/ui/switch"
@@ -323,7 +323,7 @@ export default function EditArticlePage() {
323323
<div className="mt-4">
324324
<p className="font-medium mb-2">Image Preview:</p>
325325
<img
326-
src={img || "/placeholder.svg"}
326+
src={sanitizeUrl(img) || "/placeholder.svg"}
327327
alt={imgAlt || "Image Preview"}
328328
className="max-h-64 object-contain border border-white/20"
329329
onError={(e) => {

app/admin/articles/new/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
1515
import { Loader2 } from "lucide-react"
1616
import { marked } from "marked"
1717
import DOMPurify from "dompurify"
18-
import { generateSlugFromTitle } from "@/lib/utils"
18+
import { generateSlugFromTitle, sanitizeUrl } from "@/lib/utils"
1919
import { MarkdownToolbar } from "@/components/markdown-toolbar"
2020

2121
// Type for an author document
@@ -348,7 +348,7 @@ export default function NewArticlePage() {
348348
<div className="mt-4">
349349
<p className="font-medium mb-2">Image Preview:</p>
350350
<img
351-
src={img || "/placeholder.svg"}
351+
src={sanitizeUrl(img) || "/placeholder.svg"}
352352
alt={imgAlt || "Image Preview"}
353353
className="max-h-64 object-contain border border-white/20"
354354
onError={(e) => {

app/admin/team/[id]/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Input } from "@/components/ui/input"
1313
import { useToast } from "@/hooks/use-toast"
1414
import { Breadcrumb } from "@/components/breadcrumb"
1515
import { Loader2, AlertTriangle, RefreshCw, Plus, X } from "lucide-react"
16+
import { sanitizeUrl } from "@/lib/utils"
1617

1718
interface Member {
1819
name: string
@@ -304,7 +305,7 @@ export default function EditTeamMemberPage() {
304305
<div className="w-40 h-40 rounded-none overflow-hidden flex items-center justify-center border border-white/20">
305306
{avatarPreview ? (
306307
<img
307-
src={avatarPreview || "/placeholder.svg"}
308+
src={sanitizeUrl(avatarPreview) || "/placeholder.svg"}
308309
alt={member.imgAlt || member.name}
309310
className="w-full h-full object-cover"
310311
onError={(e) => {

app/admin/team/new/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Input } from "@/components/ui/input"
1010
import { useToast } from "@/hooks/use-toast"
1111
import { Breadcrumb } from "@/components/breadcrumb"
1212
import { Loader2, AlertTriangle, RefreshCw, Plus, X, UserCircle } from "lucide-react"
13-
import { generateSlugFromTitle } from "@/lib/utils"
13+
import { generateSlugFromTitle, sanitizeUrl } from "@/lib/utils"
1414

1515
export default function NewTeamMemberPage() {
1616
const [role, setRole] = useState("")
@@ -268,7 +268,7 @@ export default function NewTeamMemberPage() {
268268
<div className="w-40 h-40 rounded-none overflow-hidden flex items-center justify-center border border-white/20">
269269
{avatar ? (
270270
<img
271-
src={avatar || "/placeholder.svg"}
271+
src={sanitizeUrl(avatar) || "/placeholder.svg"}
272272
alt={imgAlt || "Profile preview"}
273273
className="w-full h-full object-cover"
274274
onError={(e) => {

lib/utils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,20 @@ export function generateSlugFromTitle(title: string): string {
3838
.trim()
3939
}
4040

41+
export function sanitizeUrl(url: string): string {
42+
if (!url) return ""
43+
const trimmed = url.trim()
44+
45+
// Allow relative paths
46+
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
47+
return trimmed
48+
}
49+
50+
try {
51+
const parsed = new URL(trimmed)
52+
return ["http:", "https:"].includes(parsed.protocol) ? trimmed : ""
53+
} catch {
54+
return ""
55+
}
56+
}
57+

0 commit comments

Comments
 (0)