feat: add created_at field to CreatedApiKeyResponse and update related handlers
This commit is contained in:
@@ -105,6 +105,7 @@ pub struct ErrorResponse {
|
||||
pub struct CreatedApiKeyResponse {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
/// Raw API key — shown only once at creation
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ pub async fn post_api_key(
|
||||
Ok(Json(CreatedApiKeyResponse {
|
||||
id: key.id.as_uuid(),
|
||||
name: key.name,
|
||||
created_at: key.created_at,
|
||||
key: raw,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -9,9 +9,7 @@ export default async function ApiKeysPage() {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
const initialApiKeys = await getApiKeys(token).catch(() => ({
|
||||
keys: [],
|
||||
}));
|
||||
const initialApiKeys = await getApiKeys(token).catch(() => []);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -21,7 +19,7 @@ export default async function ApiKeysPage() {
|
||||
Manage API keys for third-party applications.
|
||||
</p>
|
||||
</div>
|
||||
<ApiKeyList initialApiKeys={initialApiKeys.keys} />
|
||||
<ApiKeyList initialApiKeys={initialApiKeys} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -190,13 +190,13 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="profile-card__info" className="mt-4">
|
||||
<h1 id="profile-card__name" className="text-2xl font-bold">
|
||||
<div id="profile-card__info" className="mt-4 min-w-0">
|
||||
<h1 id="profile-card__name" className="text-2xl font-bold break-words">
|
||||
{user.displayName || user.username}
|
||||
</h1>
|
||||
<p
|
||||
id="profile-card__username"
|
||||
className="text-sm text-muted-foreground"
|
||||
className="text-sm text-muted-foreground break-all"
|
||||
>
|
||||
@{user.username}
|
||||
</p>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"use client"
|
||||
|
||||
import { useOptimistic, useRef } from "react"
|
||||
import { followUser, unfollowUser } from "@/app/actions/social"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { followUser, unfollowUser } from "@/lib/api"
|
||||
import { useAuth } from "@/hooks/use-auth"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { toast } from "sonner"
|
||||
import { UserPlus, UserMinus } from "lucide-react"
|
||||
@@ -68,8 +70,11 @@ function burstParticles(canvas: HTMLCanvasElement) {
|
||||
export function FollowButton({ username, isInitiallyFollowing }: FollowButtonProps) {
|
||||
const [optimisticFollowing, setOptimisticFollowing] = useOptimistic(isInitiallyFollowing)
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
const { token } = useAuth()
|
||||
const router = useRouter()
|
||||
|
||||
async function handleClick() {
|
||||
if (!token) return
|
||||
const next = !optimisticFollowing
|
||||
setOptimisticFollowing(next)
|
||||
|
||||
@@ -78,7 +83,8 @@ export function FollowButton({ username, isInitiallyFollowing }: FollowButtonPro
|
||||
}
|
||||
|
||||
try {
|
||||
await (next ? followUser(username) : unfollowUser(username))
|
||||
await (next ? followUser(username, token) : unfollowUser(username, token))
|
||||
router.refresh()
|
||||
} catch {
|
||||
setOptimisticFollowing(!next)
|
||||
toast.error(`Failed to ${next ? "follow" : "unfollow"} user.`)
|
||||
|
||||
@@ -455,7 +455,7 @@ export const search = (query: string, token: string | null) =>
|
||||
// ── API Keys ──────────────────────────────────────────────────────────────
|
||||
|
||||
export const getApiKeys = (token: string) =>
|
||||
apiFetch("/api-keys", { next: { tags: ['api-keys'] } }, z.object({ keys: z.array(ApiKeySchema) }), token);
|
||||
apiFetch("/api-keys", { next: { tags: ['api-keys'] } }, z.array(ApiKeySchema), token);
|
||||
|
||||
export const createApiKey = (data: z.infer<typeof CreateApiKeySchema>, token: string) =>
|
||||
apiFetch("/api-keys", { method: "POST", body: JSON.stringify(data) }, ApiKeyResponseSchema, token);
|
||||
|
||||
@@ -15,6 +15,24 @@ export async function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
const parts = pathname.split("/");
|
||||
|
||||
if (parts[1] === "media") {
|
||||
const apiBase =
|
||||
process.env.NEXT_PUBLIC_SERVER_SIDE_API_URL ?? "http://api:8000";
|
||||
const forwardHeaders: Record<string, string> = {};
|
||||
for (const [key, value] of request.headers.entries()) {
|
||||
if (key.toLowerCase() !== "host") forwardHeaders[key] = value;
|
||||
}
|
||||
const res = await fetch(`${apiBase}${pathname}`, { headers: forwardHeaders });
|
||||
const body = await res.arrayBuffer();
|
||||
return new NextResponse(body, {
|
||||
status: res.status,
|
||||
headers: {
|
||||
"content-type": res.headers.get("content-type") ?? "application/octet-stream",
|
||||
"cache-control": res.headers.get("cache-control") ?? "public, max-age=31536000, immutable",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (parts.length >= 3 && parts[1] === "users") {
|
||||
const segment = decodeURIComponent(parts[2]);
|
||||
const accept = request.headers.get("accept") ?? "";
|
||||
@@ -71,5 +89,5 @@ export async function middleware(request: NextRequest) {
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: "/users/:path*",
|
||||
matcher: ["/users/:path*", "/media/:path*"],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user