auth,feeds,ai,ui,deploy: build Stellar

Use Effect services for requests, AI tasks, and the RSS worker.
Add admin accounts, feed sharing, and stored reading profiles.
Add Biome, Effect tools, and Podman deployment with Caddy.
This commit is contained in:
EFLKumo committed 2026-10-11 16:21:39 +08:00
1 parent ba8f925eed
commit ce87e56053
137 files changed
+14652 -2457

No files matched your search

+233
View File
@@ -0,0 +1,233 @@
import AxeBuilder from "@axe-core/playwright"
import { expect, test } from "@playwright/test"
// Run against an isolated database with the initial administrator account.
const adminIdentifier = process.env.TEST_ADMIN_EMAIL ?? "admin@example.com"
const adminPassword =
process.env.TEST_ADMIN_PASSWORD ?? "stellar-test-password-0001"
test("collect, curate, and share a feed across two accounts", async ({
page,
}) => {
const errors: string[] = []
let signedIn = false
page.on("pageerror", (error) => errors.push(error.message))
page.on("console", (message) => {
if (message.type() !== "error") return
if (
!signedIn &&
message.text().includes("401") &&
message.location().url.includes("/api/app")
)
return
errors.push(message.text())
})
const suffix = Date.now().toString(36)
const title = `Reading notes ${suffix}`
const personName = `Reader ${suffix}`
const username = `reader_${suffix}`
const preferences = `I like research about software reliability and clear interface design. Reading list: ${suffix}.`
await page.goto("/")
await page.getByLabel("Username or email").fill(adminIdentifier)
await page.getByLabel("Password", { exact: true }).fill(adminPassword)
await page.getByRole("button", { name: "Sign in", exact: true }).click()
await expect(
page.getByRole("heading", { name: "Your feed", exact: true }),
).toBeVisible()
signedIn = true
await page.getByRole("button", { name: "Add feed", exact: true }).click()
const submit = page.getByRole("dialog", { name: "Add to your feed" })
await submit.getByRole("button", { name: "Markdown", exact: true }).click()
await submit.getByLabel("Title", { exact: true }).fill(title)
await submit
.getByLabel("Markdown", { exact: true })
.fill(
"## A note on reliable systems\n\nSmall services are easier to inspect. **Cancellation matters.**\n\n- Keep state explicit.\n- Test user actions.",
)
await submit.getByRole("button", { name: "Add feed", exact: true }).click()
const reader = page.getByRole("dialog", { name: title, exact: true })
await expect(
reader.getByRole("heading", { name: "A note on reliable systems" }),
).toBeVisible()
await reader.getByRole("button", { name: "Save", exact: true }).click()
await expect(
reader.getByRole("button", { name: "Saved", exact: true }),
).toBeVisible()
await page.keyboard.press("Escape")
await expect(reader).not.toBeVisible()
await page
.getByRole("button", { name: "Saved for later", exact: true })
.click()
await expect(page.getByRole("link").filter({ hasText: title })).toBeVisible()
await page
.getByRole("button", { name: "Sources & preferences", exact: true })
.click()
await page.getByLabel("Your reading preferences").fill(preferences)
await page
.getByRole("button", { name: "Save preferences", exact: true })
.click()
await expect(
page.getByRole("button", { name: "Save preferences", exact: true }),
).toBeDisabled()
await page
.getByRole("button", { name: "Add RSS source", exact: true })
.click()
const sourceDialog = page.getByRole("dialog", { name: "Add an RSS source" })
await sourceDialog
.getByLabel("RSS or Atom URL")
.fill(`https://1.1.1.1/feed.xml?test=${suffix}`)
await sourceDialog.getByLabel("Name (optional)").fill(`Research ${suffix}`)
await sourceDialog
.getByRole("button", { name: "Add source", exact: true })
.click()
const sourceSwitch = page.getByRole("switch", {
name: `Enable Research ${suffix}`,
})
await expect(sourceSwitch).toBeChecked()
await sourceSwitch.click()
await expect(sourceSwitch).not.toBeChecked()
await page.getByRole("button", { name: `Remove Research ${suffix}` }).click()
await expect(sourceSwitch).not.toBeVisible()
await page.reload()
await page
.getByRole("button", { name: "Sources & preferences", exact: true })
.click()
await expect(page.getByLabel("Your reading preferences")).toHaveValue(
preferences,
)
await page
.getByRole("button", { name: "Stellar assistant", exact: true })
.click()
await page.getByRole("button", { name: "New chat", exact: true }).click()
await expect(page.getByLabel("Message Stellar")).toBeVisible()
const conversationId = await page
.getByRole("combobox", { name: "Conversation" })
.inputValue()
expect(conversationId).not.toBe("")
await page.reload()
await page
.getByRole("button", { name: "Stellar assistant", exact: true })
.click()
await expect(
page.getByRole("combobox", { name: "Conversation" }),
).toHaveValue(conversationId)
await expect(page.getByLabel("Message Stellar")).toBeVisible()
await page
.getByRole("button", { name: "Delete conversation", exact: true })
.click()
await expect(
page.getByText("Follow a thought", { exact: true }),
).toBeVisible()
await page.getByRole("button", { name: "People", exact: true }).click()
await page
.getByRole("button", { name: "Create account", exact: true })
.click()
const userDialog = page.getByRole("dialog", { name: "Create an account" })
await userDialog.getByLabel("Name", { exact: true }).fill(personName)
await userDialog.getByLabel("Username", { exact: true }).fill(username)
await userDialog
.getByLabel("Email", { exact: true })
.fill(`${username}@example.com`)
await userDialog.getByLabel("Password", { exact: true }).fill(adminPassword)
await userDialog
.getByRole("button", { name: "Create account", exact: true })
.click()
await expect(
page.getByRole("cell", { name: personName, exact: true }),
).toBeVisible()
await page.getByRole("button", { name: "Your feed", exact: true }).click()
await page.getByRole("link").filter({ hasText: title }).click()
await reader.getByRole("button", { name: "Forward", exact: true }).click()
const forward = page.getByRole("dialog", { name: "Forward this feed" })
await forward
.getByLabel("Send to")
.selectOption({ label: `${personName} (@${username})` })
await forward
.getByRole("button", { name: "Forward & make public", exact: true })
.click()
await expect(reader.getByText("Public", { exact: true })).toBeVisible()
await reader
.getByLabel("Your comment")
.fill("This is a useful idea to discuss.")
await reader
.getByRole("button", { name: "Post comment", exact: true })
.click()
await expect(
reader.getByText("This is a useful idea to discuss.", { exact: true }),
).toBeVisible()
await expect(reader).toHaveCSS("width", "672px")
const desktopAccess = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.analyze()
expect
.soft(
desktopAccess.violations.map((violation) => ({
id: violation.id,
nodes: violation.nodes.map((node) => node.target),
})),
)
.toEqual([])
await page.screenshot({
path: "/tmp/stellar-app-desktop.png",
fullPage: true,
})
await page.keyboard.press("Escape")
await page.getByRole("button", { name: "Sign out", exact: true }).click()
await page.getByLabel("Username or email").fill(username)
await page.getByLabel("Password", { exact: true }).fill(adminPassword)
await page.getByRole("button", { name: "Sign in", exact: true }).click()
await expect(page.getByRole("link").filter({ hasText: title })).toBeVisible()
await page
.getByRole("button", { name: "Sources & preferences", exact: true })
.click()
await expect(page.getByLabel("Your reading preferences")).toHaveValue("")
await expect(
page.getByRole("button", { name: "People", exact: true }),
).not.toBeVisible()
await page.getByRole("button", { name: "Your feed", exact: true }).click()
await page.setViewportSize({ width: 390, height: 844 })
await expect(page.getByRole("link").filter({ hasText: title })).toBeVisible()
const overflows = await page.evaluate(
() => document.documentElement.scrollWidth > window.innerWidth,
)
expect(overflows).toBe(false)
await page.screenshot({ path: "/tmp/stellar-app-mobile.png", fullPage: true })
await page.getByRole("button", { name: "Toggle Sidebar" }).click()
await page.getByRole("button", { name: "Public space", exact: true }).click()
await expect(
page.getByRole("heading", { name: "Public space", exact: true }),
).toBeVisible()
await page.getByRole("link").filter({ hasText: title }).click()
await expect(
reader.getByText("This is a useful idea to discuss.", { exact: true }),
).toBeVisible()
await expect(
reader.getByRole("heading", { name: "A note on reliable systems" }),
).toBeVisible()
await expect(reader).toHaveCSS("width", "390px")
const mobileAccess = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.analyze()
expect
.soft(
mobileAccess.violations.map((violation) => ({
id: violation.id,
nodes: violation.nodes.map((node) => node.target),
})),
)
.toEqual([])
await page.screenshot({
path: "/tmp/stellar-app-mobile-reader.png",
fullPage: true,
})
expect(errors).toEqual([])
})
+392
View File
@@ -0,0 +1,392 @@
import assert from "node:assert/strict"
import { randomUUID } from "node:crypto"
import { after, before, describe, it } from "node:test"
import { and, eq } from "drizzle-orm"
import { drizzle } from "drizzle-orm/postgres-js"
import postgres from "postgres"
import { feeds, user } from "../src/server/schema"
import type { AppData, FeedDetail, ProfileView } from "../src/shared/contracts"
const configuredBase = process.env.TEST_BASE_URL
const base = configuredBase?.startsWith("http")
? configuredBase
: `http://${configuredBase ?? "localhost:3000"}`
const origin = new URL(base).origin
const password = "stellar-integration-password-0001"
const suffix = randomUUID().slice(0, 8)
const accounts = [
{
username: `test_a_${suffix}`,
email: `test_a_${suffix}@example.com`,
name: "Test Alice",
},
{
username: `test_b_${suffix}`,
email: `test_b_${suffix}@example.com`,
name: "Test Bob",
},
]
async function request<T = Record<string, unknown>>(
path: string,
body: unknown,
cookie = "",
) {
const response = await fetch(`${base}${path}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Origin: origin,
Cookie: cookie,
},
body: JSON.stringify(body),
signal: AbortSignal.timeout(20_000),
})
const bodyText = await response.text()
let responseBody: unknown
try {
responseBody = JSON.parse(bodyText)
} catch {
responseBody = { error: bodyText }
}
return {
status: response.status,
body: responseBody as T,
cookie: response.headers
.getSetCookie()
.map((entry) => entry.split(";")[0])
.join("; "),
}
}
describe("Stellar HTTP and PostgreSQL integration", {
skip: !configuredBase,
concurrency: false,
}, () => {
let adminCookie = ""
let aliceCookie = ""
let bobCookie = ""
let aliceId = ""
let bobId = ""
let privateId = ""
let publicId = ""
let threadId = ""
let client: ReturnType<typeof postgres> | undefined
const createdAccounts: string[] = []
before(async () => {
const databaseUrl =
process.env.TEST_DATABASE_URL ?? process.env.DATABASE_URL
assert.ok(
databaseUrl,
"Set TEST_DATABASE_URL or DATABASE_URL for integration tests.",
)
assert.ok(
process.env.STELLAR_ADMIN_USERNAME,
"Set STELLAR_ADMIN_USERNAME for integration tests.",
)
assert.ok(
process.env.STELLAR_ADMIN_PASSWORD,
"Set STELLAR_ADMIN_PASSWORD for integration tests.",
)
client = postgres(databaseUrl, {
max: 2,
connect_timeout: 5,
idle_timeout: 5,
})
const admin = await request("/api/auth/sign-in/username", {
username: process.env.STELLAR_ADMIN_USERNAME,
password: process.env.STELLAR_ADMIN_PASSWORD,
})
assert.equal(admin.status, 200, JSON.stringify(admin.body))
assert.ok(admin.cookie)
adminCookie = admin.cookie
for (const account of accounts) {
const created = await request<{ id: string }>(
"/api/app",
{
action: "createUser",
...account,
password,
role: "user",
},
adminCookie,
)
assert.equal(created.status, 200, JSON.stringify(created.body))
createdAccounts.push(created.body.id)
}
;[aliceId, bobId] = createdAccounts
const alice = await request("/api/auth/sign-in/username", {
username: accounts[0].username,
password,
})
const bob = await request("/api/auth/sign-in/username", {
username: accounts[1].username,
password,
})
assert.equal(alice.status, 200)
assert.equal(bob.status, 200)
aliceCookie = alice.cookie
bobCookie = bob.cookie
assert.ok(aliceCookie && bobCookie)
})
after(async () => {
if (!client) return
try {
const db = drizzle(client)
for (const id of createdAccounts)
await db.delete(user).where(eq(user.id, id))
} finally {
await client.end({ timeout: 5 })
}
})
it("requires authentication and permits account creation only for administrators", async () => {
const unauthenticated = await request("/api/app", { action: "bootstrap" })
assert.equal(unauthenticated.status, 401)
const signup = await request("/api/auth/sign-up/email", {
email: `signup_${suffix}@example.com`,
name: "Blocked Signup",
password,
})
assert.ok(signup.status >= 400, JSON.stringify(signup.body))
const create = await request(
"/api/app",
{
action: "createUser",
email: `forbidden_${suffix}@example.com`,
username: `forbidden_${suffix}`,
name: "Forbidden User",
password,
role: "admin",
},
bobCookie,
)
assert.equal(create.status, 403)
const list = await request("/api/app", { action: "listUsers" }, bobCookie)
assert.equal(list.status, 403)
const emailLogin = await request("/api/auth/sign-in/email", {
email: accounts[0].email,
password,
})
assert.equal(emailLogin.status, 200)
})
it("keeps private feeds and all related operations inaccessible to other users", async () => {
const created = await request<{ id: string; duplicate: boolean }>(
"/api/app",
{
action: "submitFeed",
title: `Private ${suffix}`,
content: "# Private text\nOnly Alice can read this feed.",
},
aliceCookie,
)
assert.equal(created.status, 200)
assert.equal(created.body.duplicate, false)
privateId = created.body.id
const detail = await request<FeedDetail>(
"/api/app",
{ action: "getFeed", id: privateId },
aliceCookie,
)
assert.equal(detail.status, 200)
assert.equal(detail.body.isPublic, false)
for (const command of [
{ action: "getFeed", id: privateId },
{ action: "comment", id: privateId, content: "Forbidden comment" },
{ action: "saveFeed", id: privateId, saved: true },
{ action: "feedback", id: privateId, interested: true },
{ action: "forwardFeed", id: privateId, toUserId: aliceId },
]) {
const result = await request("/api/app", command, bobCookie)
assert.equal(result.status, 404, command.action)
}
})
it("publishes forwarded feeds and permits comments on the public feed", async () => {
const forwarded = await request<{ id: string }>(
"/api/app",
{ action: "forwardFeed", id: privateId, toUserId: bobId },
aliceCookie,
)
assert.equal(forwarded.status, 200)
publicId = forwarded.body.id
const publicFeed = await request<FeedDetail>(
"/api/app",
{ action: "getFeed", id: publicId },
bobCookie,
)
assert.equal(publicFeed.status, 200)
assert.equal(publicFeed.body.isPublic, true)
const comment = await request(
"/api/app",
{ action: "comment", id: publicId, content: `Public comment ${suffix}` },
bobCookie,
)
assert.equal(comment.status, 200)
const updated = await request<FeedDetail>(
"/api/app",
{ action: "getFeed", id: publicId },
aliceCookie,
)
assert.ok(
updated.body.comments.some(
(reply) => reply.content === `Public comment ${suffix}`,
),
)
})
it("stores preferences and conversation ownership in PostgreSQL", async () => {
const preferences = `Solar physics and developer tools ${suffix}`
const changed = await request<ProfileView>(
"/api/app",
{ action: "updatePreferences", preferences },
aliceCookie,
)
assert.equal(changed.status, 200)
assert.equal(changed.body.preferences, preferences)
const alice = await request<AppData>(
"/api/app",
{ action: "bootstrap" },
aliceCookie,
)
const bob = await request<AppData>(
"/api/app",
{ action: "bootstrap" },
bobCookie,
)
assert.equal(alice.body.profile.preferences, preferences)
assert.notEqual(bob.body.profile.preferences, preferences)
const conversation = await request<{ id: string }>(
"/api/app",
{ action: "createThread" },
aliceCookie,
)
assert.equal(conversation.status, 200)
threadId = conversation.body.id
const forbiddenRead = await request(
"/api/app",
{ action: "getThread", id: threadId },
bobCookie,
)
const forbiddenDelete = await request(
"/api/app",
{ action: "deleteThread", id: threadId },
bobCookie,
)
assert.equal(forbiddenRead.status, 404)
assert.equal(forbiddenDelete.status, 404)
const stored = await request<{ id: string; messages: unknown[] }>(
"/api/app",
{ action: "getThread", id: threadId },
aliceCookie,
)
assert.equal(stored.status, 200)
assert.equal(stored.body.id, threadId)
assert.deepEqual(stored.body.messages, [])
})
it("blocks local, metadata, and IPv6 destinations before article or RSS access", async () => {
for (const url of [
"http://localhost/article",
"http://127.0.0.1/article",
"http://169.254.169.254/latest/meta-data",
"http://[::1]/article",
"http://[::ffff:127.0.0.1]/article",
]) {
for (const action of ["submitFeed", "subscribe"]) {
const result = await request("/api/app", { action, url }, aliceCookie)
assert.equal(result.status, 400, `${action}: ${url}`)
}
}
})
it("rejects unknown AI models and reports unavailable provider keys", async () => {
const unknown = await request(
"/api/app",
{ action: "summarize", id: publicId, modelId: "unknown-model" },
aliceCookie,
)
assert.equal(unknown.status, 400)
const data = await request<AppData>(
"/api/app",
{ action: "bootstrap" },
aliceCookie,
)
const modelId = data.body.models[0]?.id
assert.ok(modelId)
const missing = await request(
"/api/app",
{ action: "summarize", id: publicId, modelId },
aliceCookie,
)
assert.equal(missing.status, 503)
assert.match(String(missing.body.error), /API key is not set/)
})
it("serializes publication and redirects submissions to the existing public URL", async () => {
assert.ok(client)
const db = drizzle(client)
const url = `https://example.com/stellar-integration-${suffix}`
const first = randomUUID()
const second = randomUUID()
await db.insert(feeds).values([
{
id: first,
authorId: aliceId,
title: "First private link",
url,
content: "First article.",
kind: "link",
origin: "user",
},
{
id: second,
authorId: bobId,
title: "Second private link",
url,
content: "Second article.",
kind: "link",
origin: "user",
},
])
const [one, two] = await Promise.all([
request<{ id: string }>(
"/api/app",
{ action: "forwardFeed", id: first, toUserId: bobId },
aliceCookie,
),
request<{ id: string }>(
"/api/app",
{ action: "forwardFeed", id: second, toUserId: aliceId },
bobCookie,
),
])
assert.equal(one.status, 200, JSON.stringify(one.body))
assert.equal(two.status, 200, JSON.stringify(two.body))
assert.equal(one.body.id, two.body.id)
const publicRows = await db
.select({ id: feeds.id })
.from(feeds)
.where(and(eq(feeds.url, url), eq(feeds.isPublic, true)))
assert.equal(publicRows.length, 1)
const duplicate = await request<{ id: string; duplicate: boolean }>(
"/api/app",
{
action: "submitFeed",
url: `${url}?utm_source=integration#read`,
},
adminCookie,
)
assert.equal(duplicate.status, 200)
assert.equal(duplicate.body.duplicate, true)
assert.equal(duplicate.body.id, one.body.id)
const totalRows = await db
.select({ id: feeds.id })
.from(feeds)
.where(eq(feeds.url, url))
assert.equal(totalRows.length, 2)
})
})