S3+S4: user param auth, static file serving, Docker deploy config
- replace hardcoded benji with ?user= query param - add GET /api/users and GET /api/me?user= endpoints - serve frontend static files via tower-http ServeDir - add multi-stage Dockerfile (Rust + Vite → single image) - add docker-compose.yml + Caddyfile for apes.unslope.com - frontend: getCurrentUsername() from URL param → localStorage - sidebar shows current user Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import type { Channel } from "@/types/Channel";
|
||||
import type { Message } from "@/types/Message";
|
||||
import { getChannels, getMessages } from "@/api";
|
||||
import { getChannels, getMessages, getCurrentUsername } from "@/api";
|
||||
import { ChannelSidebar } from "@/components/ChannelSidebar";
|
||||
import { MessageItem } from "@/components/MessageItem";
|
||||
import { ComposeBox } from "@/components/ComposeBox";
|
||||
|
||||
@@ -1,22 +1,46 @@
|
||||
import type { Channel } from "./types/Channel";
|
||||
import type { Message } from "./types/Message";
|
||||
import type { User } from "./types/User";
|
||||
import type { CreateChannel } from "./types/CreateChannel";
|
||||
import type { PostMessage } from "./types/PostMessage";
|
||||
|
||||
const BASE = "/api";
|
||||
|
||||
// Get current user from URL param or localStorage
|
||||
export function getCurrentUsername(): string {
|
||||
const url = new URL(window.location.href);
|
||||
const param = url.searchParams.get("user");
|
||||
if (param) {
|
||||
localStorage.setItem("colony_user", param);
|
||||
return param;
|
||||
}
|
||||
return localStorage.getItem("colony_user") || "benji";
|
||||
}
|
||||
|
||||
function userQuery(): string {
|
||||
return `user=${encodeURIComponent(getCurrentUsername())}`;
|
||||
}
|
||||
|
||||
async function json<T>(res: Response): Promise<T> {
|
||||
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function getMe(): Promise<User> {
|
||||
return json(await fetch(`${BASE}/me?${userQuery()}`));
|
||||
}
|
||||
|
||||
export async function getUsers(): Promise<User[]> {
|
||||
return json(await fetch(`${BASE}/users`));
|
||||
}
|
||||
|
||||
export async function getChannels(): Promise<Channel[]> {
|
||||
return json(await fetch(`${BASE}/channels`));
|
||||
}
|
||||
|
||||
export async function createChannel(body: CreateChannel): Promise<Channel> {
|
||||
return json(
|
||||
await fetch(`${BASE}/channels`, {
|
||||
await fetch(`${BASE}/channels?${userQuery()}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
@@ -26,10 +50,10 @@ export async function createChannel(body: CreateChannel): Promise<Channel> {
|
||||
|
||||
export async function getMessages(
|
||||
channelId: string,
|
||||
params?: { since?: string; type?: string; user_id?: string },
|
||||
params?: { after_seq?: number; type?: string; user_id?: string },
|
||||
): Promise<Message[]> {
|
||||
const query = new URLSearchParams();
|
||||
if (params?.since) query.set("since", params.since);
|
||||
if (params?.after_seq) query.set("after_seq", String(params.after_seq));
|
||||
if (params?.type) query.set("type", params.type);
|
||||
if (params?.user_id) query.set("user_id", params.user_id);
|
||||
const qs = query.toString();
|
||||
@@ -43,7 +67,7 @@ export async function postMessage(
|
||||
body: PostMessage,
|
||||
): Promise<Message> {
|
||||
return json(
|
||||
await fetch(`${BASE}/channels/${channelId}/messages`, {
|
||||
await fetch(`${BASE}/channels/${channelId}/messages?${userQuery()}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import type { Channel } from "@/types/Channel";
|
||||
import { createChannel } from "@/api";
|
||||
import { createChannel, getCurrentUsername } from "@/api";
|
||||
|
||||
interface Props {
|
||||
channels: Channel[];
|
||||
@@ -61,6 +61,12 @@ export function ChannelSidebar({
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Current user */}
|
||||
<div className="px-3 py-2 border-t border-sidebar-border text-[11px]">
|
||||
<span className="text-muted-foreground">logged in as </span>
|
||||
<span className="font-bold text-foreground">{getCurrentUsername()}</span>
|
||||
</div>
|
||||
|
||||
{/* New channel input */}
|
||||
<div className="p-2 border-t border-sidebar-border">
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user