feat: expand profiles and weapon configuration
Validate and deploy production / validate (push) Successful in 26s
Validate and deploy staging / validate (push) Successful in 25s
Validate and deploy production / deploy (push) Failing after 1m10s
Validate and deploy staging / deploy (push) Failing after 8s

This commit is contained in:
JB
2026-07-28 00:11:40 -04:00
parent 0b3be3587b
commit a434cbe60c
37 changed files with 2209 additions and 800 deletions
+315 -50
View File
@@ -14,6 +14,16 @@ import { randomUUID } from 'node:crypto';
import { queueCurrentAvatarReview } from '$lib/server/avatars';
import { agentProfileHref } from '$lib/agents';
import { resolveAgentProfile } from '$lib/server/agent-profiles';
import {
orderProfileCommentThread,
profileCommentNotificationRecipient
} from '$lib/server/profile-comment-thread';
import {
notifyProfileCommentFollowers,
profileCommentConversation
} from '$lib/server/profile-comment-follows';
import { loadAccountControlData } from '$lib/server/account-control-data';
import { saveProfileBio, updateProfileVisibility } from '$lib/server/profile-settings';
import type { Actions, PageServerLoad } from './$types';
const COMMENT_MAX_CHARACTERS = 2_000;
@@ -24,17 +34,23 @@ const unavailable = (row: Record<string, unknown> | null | undefined) => {
return !row || Boolean(row.deleted_at || row.blocked_at || banned);
};
const commentHref = (targetType: string, slug: unknown, targetId: string, targetPublicSlug: unknown) => {
const commentHref = (
targetType: string,
slug: unknown,
targetId: string,
targetPublicSlug: unknown,
commentId: string
) => {
if (targetType === 'profile') {
return agentProfileHref({
id: targetId,
publicSlug: typeof targetPublicSlug === 'string' ? targetPublicSlug : null
}, 'profile-comments');
}, `profile-comment-${commentId}`);
}
return `/builds/${encodeURIComponent(String(slug ?? ''))}#comments`;
return `/builds/${encodeURIComponent(String(slug ?? ''))}#comment-${encodeURIComponent(commentId)}`;
};
export const load: PageServerLoad = async ({ params, cookies }) => {
export const load: PageServerLoad = async ({ params, cookies, url }) => {
const database = await getDatabase();
const viewer = await getSessionUser(cookies);
const resolved = await resolveAgentProfile(database, params.id);
@@ -43,7 +59,7 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
const agent = await (await database.prepare(`SELECT users.id,
COALESCE(profile.display_name,users.username) AS display_name,
profile.public_slug,profile.bio_markdown,profile.is_public,profile.bio_updated_at,
COALESCE(moderation.bio_hidden,0) AS bio_hidden,
COALESCE(moderation.bio_hidden,0) AS bio_hidden,moderation.reason AS moderation_reason,
avatar.source AS avatar_source,avatar.value AS avatar_value,avatar.updated_at AS avatar_updated_at,
status.blocked_at,status.banned_until,deleted.deleted_at
FROM users
@@ -61,6 +77,18 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
if (params.id !== String(agent.public_slug)) {
redirect(308, agentProfileHref({ id: agentId, publicSlug: String(agent.public_slug) }));
}
const [pendingBioRevision, accountControl] = isSelf
? await Promise.all([
(await database.prepare(`SELECT revision.body_markdown,revision.created_at,scan.reason
FROM moderated_content_revisions revision
JOIN content_moderation_scans scan ON scan.revision_id=revision.id
LEFT JOIN content_moderation_reviews review ON review.scan_id=scan.id
WHERE revision.content_type='profile_bio' AND revision.content_id=?
AND scan.status IN ('review','priority_review','error') AND review.id IS NULL
ORDER BY revision.created_at DESC LIMIT 1`)).get(agentId),
loadAccountControlData(database, agentId, url.searchParams.get('control'))
])
: [null, null];
const [roleRows, buildRows, guideRows, authoredCommentRows, profileCommentRows, authoredCount, profileCommentCount, contentCounts] =
await Promise.all([
@@ -76,7 +104,7 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
FROM builds WHERE owner_id=? AND visibility='public'
AND guide_body IS NOT NULL AND length(trim(guide_body))>0
ORDER BY updated_at DESC LIMIT 60`)).all(agentId),
(await database.prepare(`SELECT comments.id,comments.body_markdown,comments.created_at,
(await database.prepare(`SELECT comments.id,comments.created_at,
thread.target_type,thread.target_id,builds.title AS build_title,builds.slug,
COALESCE(target_profile.display_name,target_user.username) AS target_display_name,
target_profile.public_slug AS target_public_slug
@@ -94,8 +122,9 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
AND (target_status.banned_until IS NULL OR target_status.banned_until<=?))
)
ORDER BY comments.created_at DESC LIMIT 50`)).all(agentId, new Date().toISOString()),
(await database.prepare(`SELECT comments.id,comments.author_id,comments.body_markdown,
comments.created_at,COALESCE(profile.display_name,users.username,'Deleted Agent') AS display_name,
(await database.prepare(`SELECT comments.id,comments.author_id,comments.parent_id,comments.body_markdown,
comments.status,comments.created_at,
COALESCE(profile.display_name,users.username,'Deleted Agent') AS display_name,
profile.public_slug,
avatar.source AS avatar_source,avatar.value AS avatar_value,avatar.updated_at AS avatar_updated_at
FROM comment_threads thread
@@ -128,6 +157,86 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
FROM builds WHERE owner_id=? AND visibility='public'`)).get(agentId)
]);
const loadedCommentIds = new Set(profileCommentRows.map((comment) => String(comment.id)));
let missingParentIds = [...new Set(profileCommentRows
.map((comment) => comment.parent_id ? String(comment.parent_id) : '')
.filter((parentId) => parentId && !loadedCommentIds.has(parentId)))];
for (let level = 0; missingParentIds.length && level < 100; level += 1) {
const placeholders = missingParentIds.map(() => '?').join(',');
const ancestors = await (await database.prepare(`SELECT comments.id,comments.author_id,
comments.parent_id,comments.body_markdown,comments.status,comments.created_at,
COALESCE(profile.display_name,users.username,'Deleted Agent') AS display_name,
profile.public_slug,
avatar.source AS avatar_source,avatar.value AS avatar_value,
avatar.updated_at AS avatar_updated_at
FROM comments
JOIN comment_threads thread ON thread.id=comments.thread_id
LEFT JOIN users ON users.id=comments.author_id
LEFT JOIN user_profiles profile ON profile.user_id=users.id
LEFT JOIN user_avatars avatar ON avatar.user_id=users.id
WHERE comments.id IN (${placeholders})
AND comments.status IN ('visible','removed')
AND thread.target_type='profile' AND thread.target_id=?`))
.all(...missingParentIds, agentId);
if (!ancestors.length) break;
profileCommentRows.push(...ancestors);
for (const ancestor of ancestors) loadedCommentIds.add(String(ancestor.id));
missingParentIds = [...new Set(ancestors
.map((ancestor) => ancestor.parent_id ? String(ancestor.parent_id) : '')
.filter((parentId) => parentId && !loadedCommentIds.has(parentId)))];
}
const commentAuthorIds = [...new Set(profileCommentRows
.map((comment) => comment.author_id ? String(comment.author_id) : '')
.filter(Boolean))];
const commentRoleRows = commentAuthorIds.length
? await (await database.prepare(`SELECT user_roles.user_id,roles.name
FROM user_roles
JOIN roles ON roles.id=user_roles.role_id
WHERE user_roles.user_id IN (${commentAuthorIds.map(() => '?').join(',')})
ORDER BY roles.name COLLATE NOCASE,roles.id`)).all(...commentAuthorIds)
: [];
const rolesByCommentAuthor = new Map<string, string[]>();
for (const row of commentRoleRows) {
const userId = String(row.user_id);
const roles = rolesByCommentAuthor.get(userId) ?? [];
roles.push(String(row.name));
rolesByCommentAuthor.set(userId, roles);
}
const threadedProfileComments = orderProfileCommentThread(profileCommentRows.map((comment) => {
const status = String(comment.status) === 'removed' ? 'removed' as const : 'visible' as const;
return {
id: String(comment.id),
parentId: comment.parent_id ? String(comment.parent_id) : null,
status,
bodyHtml: status === 'visible' ? renderUserMarkdown(comment.body_markdown) : '',
createdAt: String(comment.created_at),
author: {
id: String(comment.author_id),
displayName: String(comment.display_name),
publicSlug: comment.public_slug ? String(comment.public_slug) : null,
avatarUrl: avatarUrl(
comment.avatar_source,
comment.avatar_value,
String(comment.author_id),
comment.avatar_updated_at
),
roles: rolesByCommentAuthor.get(String(comment.author_id)) ?? []
},
canDelete: status === 'visible' && viewer?.id === String(comment.author_id),
canReport: status === 'visible' && Boolean(viewer) && viewer?.id !== String(comment.author_id)
};
}));
const followedRootIds = viewer && threadedProfileComments.length
? new Set((await (await database.prepare(`SELECT root_comment_id
FROM comment_thread_follows
WHERE user_id=? AND root_comment_id IN (${[...new Set(threadedProfileComments.map((comment) => comment.rootId))]
.map(() => '?').join(',')})`)).all(
viewer.id,
...[...new Set(threadedProfileComments.map((comment) => comment.rootId))]
)).map((row) => String(row.root_comment_id)))
: new Set<string>();
return {
agent: {
id: String(agent.id),
@@ -140,6 +249,16 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
agent.avatar_updated_at
),
bioHtml: agent.bio_hidden ? '' : renderUserMarkdown(agent.bio_markdown),
bioMarkdown: isSelf
? String(pendingBioRevision?.body_markdown ?? agent.bio_markdown ?? '')
: '',
isPublic: Boolean(agent.is_public),
bioHidden: Boolean(agent.bio_hidden),
bioModerationReason: isSelf && agent.moderation_reason
? String(agent.moderation_reason)
: null,
bioReviewPending: Boolean(pendingBioRevision),
bioReviewReason: pendingBioRevision?.reason ? String(pendingBioRevision.reason) : null,
roles: roleRows.map((role) => String(role.name))
},
counts: {
@@ -167,58 +286,109 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
const targetId = String(comment.target_id);
return {
id: String(comment.id),
bodyHtml: renderUserMarkdown(comment.body_markdown),
createdAt: String(comment.created_at),
targetType,
targetTitle: targetType === 'profile'
? `${String(comment.target_display_name ?? 'Agent')} profile`
: String(comment.build_title ?? 'Community content'),
targetHref: commentHref(targetType, comment.slug, targetId, comment.target_public_slug)
targetHref: commentHref(
targetType,
comment.slug,
targetId,
comment.target_public_slug,
String(comment.id)
)
};
}),
profileComments: profileCommentRows.map((comment) => ({
id: String(comment.id),
bodyHtml: renderUserMarkdown(comment.body_markdown),
createdAt: String(comment.created_at),
author: {
id: String(comment.author_id),
displayName: String(comment.display_name),
publicSlug: comment.public_slug ? String(comment.public_slug) : null,
avatarUrl: avatarUrl(
comment.avatar_source,
comment.avatar_value,
String(comment.author_id),
comment.avatar_updated_at
)
},
canDelete: viewer?.id === String(comment.author_id),
canReport: Boolean(viewer) && viewer?.id !== String(comment.author_id)
profileComments: threadedProfileComments.map((comment) => ({
...comment,
isFollowing: followedRootIds.has(comment.rootId)
})),
viewer: {
id: viewer?.id ?? null,
canComment: Boolean(viewer) && Boolean(agent.is_public),
canReportProfile: Boolean(viewer) && !isSelf,
isSelf
}
},
accountControl
};
};
export const actions: Actions = {
saveBio: async ({ params, cookies, request }) => {
const viewer = await getSessionUser(cookies);
if (!viewer) return fail(401, { bioError: 'Sign in to edit your profile.', bioResultToken: randomUUID() });
const database = await getDatabase();
const owner = await resolveAgentProfile(database, params.id);
if (!owner || owner.id !== viewer.id) {
return fail(403, { bioError: 'You can edit only your own profile.', bioResultToken: randomUUID() });
}
const result = await saveProfileBio(viewer, (await request.formData()).get('bio'));
if (!result.ok) {
return fail(400, { bioError: result.error, bioResultToken: randomUUID() });
}
return {
bioSaved: true,
bioReviewQueued: result.reviewQueued,
bioMarkdown: result.bio,
bioResultToken: randomUUID()
};
},
setProfileVisibility: async ({ params, cookies, request }) => {
const viewer = await getSessionUser(cookies);
if (!viewer) return fail(401, {
profileVisibilityError: 'Sign in to change profile visibility.',
profileVisibilityResultToken: randomUUID()
});
const database = await getDatabase();
const owner = await resolveAgentProfile(database, params.id);
if (!owner || owner.id !== viewer.id) {
return fail(403, {
profileVisibilityError: 'You can change only your own profile.',
profileVisibilityResultToken: randomUUID()
});
}
const result = await updateProfileVisibility(
viewer,
(await request.formData()).get('isPublic')
);
if (!result.ok) {
return fail(400, {
profileVisibilityError: result.error,
profileVisibilityResultToken: randomUUID()
});
}
return {
profileVisibilitySaved: true,
profileIsPublic: result.isPublic,
profileVisibilityResultToken: randomUUID()
};
},
commentProfile: async ({ params, cookies, request }) => {
const commenter = await getSessionUser(cookies);
if (!commenter) return fail(401, { profileCommentError: 'Sign in to post a profile comment.' });
const formData = await request.formData();
const parentId = String(formData.get('parentId') ?? '').trim() || null;
const commentFailure = (status: number, profileCommentError: string) => fail(status, {
profileCommentError,
profileCommentParentId: parentId,
profileCommentResultToken: randomUUID()
});
if (!commenter) return commentFailure(401, 'Sign in to post a profile comment.');
const body = normalizeUserMarkdown(
(await request.formData()).get('body'),
formData.get('body'),
COMMENT_MAX_CHARACTERS
);
if (body === null || Array.from(body).length < 2) {
return fail(400, {
profileCommentError: `Comments must contain 2–${COMMENT_MAX_CHARACTERS.toLocaleString()} visible characters.`
});
return commentFailure(
400,
`Comments must contain 2–${COMMENT_MAX_CHARACTERS.toLocaleString()} visible characters.`
);
}
const database = await getDatabase();
const owner = await resolveAgentProfile(database, params.id);
if (!owner) return fail(404, { profileCommentError: 'Agent profile not found.' });
if (!owner) return commentFailure(404, 'Agent profile not found.');
const agentId = owner.id;
const moderationPolicy = await getCommunityModerationPolicy();
const now = new Date();
@@ -233,10 +403,10 @@ export const actions: Actions = {
const commenterState = rows.find((row) => row.id === commenter.id);
const ownerState = rows.find((row) => row.id === agentId);
if (unavailable(commenterState)) {
return fail(403, { profileCommentError: 'This account cannot post comments.' });
return commentFailure(403, 'This account cannot post comments.');
}
if (unavailable(ownerState) || !Boolean(ownerState?.is_public)) {
return fail(404, { profileCommentError: 'Agent profile not found.' });
return commentFailure(404, 'Agent profile not found.');
}
const recent = await (await database.prepare(`SELECT comments.body_markdown,comments.created_at
@@ -247,10 +417,10 @@ export const actions: Actions = {
if (typeof recent?.created_at === 'string') {
const elapsed = (now.getTime() - new Date(recent.created_at).getTime()) / 1_000;
if (elapsed < RAPID_COMMENT_SECONDS) {
return fail(429, { profileCommentError: 'Wait a few seconds before posting again.' });
return commentFailure(429, 'Wait a few seconds before posting again.');
}
if (elapsed < moderationPolicy.duplicateWindowSeconds && recent.body_markdown === body) {
return fail(409, { profileCommentError: 'That comment was already posted.' });
return commentFailure(409, 'That comment was already posted.');
}
}
@@ -268,41 +438,133 @@ export const actions: Actions = {
const thread = await (await database.prepare(
"SELECT id FROM comment_threads WHERE target_type='profile' AND target_id=?"
)).get(agentId);
if (!thread?.id) return fail(500, { profileCommentError: 'The profile thread could not be opened.' });
if (!thread?.id) return commentFailure(500, 'The profile thread could not be opened.');
const parent = parentId
? await (await database.prepare(`SELECT parent.author_id
FROM comments parent
JOIN comment_threads thread ON thread.id=parent.thread_id
WHERE parent.id=? AND parent.status='visible' AND thread.id=?
AND thread.target_type='profile' AND thread.target_id=?`))
.get(parentId, thread.id, agentId)
: null;
if (parentId && !parent) {
return commentFailure(404, 'That message is no longer available for replies.');
}
const commentId = randomUUID();
await (await database.prepare(`INSERT INTO comments
(id,thread_id,author_id,parent_id,body_markdown,status,created_at,updated_at)
VALUES(?,?,?,NULL,?,?,?,?)`))
.run(commentId, thread.id, commenter.id, body, commentStatus, createdAt, createdAt);
const inserted = parentId
? await (await database.prepare(`INSERT INTO comments
(id,thread_id,author_id,parent_id,body_markdown,status,created_at,updated_at)
SELECT ?,parent.thread_id,?,parent.id,?,?,?,?
FROM comments parent
JOIN comment_threads thread ON thread.id=parent.thread_id
WHERE parent.id=? AND parent.status='visible' AND thread.id=?
AND thread.target_type='profile' AND thread.target_id=?`))
.run(
commentId,
commenter.id,
body,
commentStatus,
createdAt,
createdAt,
parentId,
thread.id,
agentId
)
: await (await database.prepare(`INSERT INTO comments
(id,thread_id,author_id,parent_id,body_markdown,status,created_at,updated_at)
VALUES(?,?,?,NULL,?,?,?,?)`))
.run(commentId, thread.id, commenter.id, body, commentStatus, createdAt, createdAt);
if (!inserted.changes) {
return commentFailure(409, 'That conversation changed before the reply could be posted.');
}
await recordCommunityModerationScan({
contentType: 'comment',
contentId: commentId,
authorId: commenter.id,
result: moderation
});
if (commentStatus === 'visible' && commenter.id !== agentId) {
const profileLink = agentProfileHref(owner, 'profile-comments');
const parentAuthorId = parent?.author_id ? String(parent.author_id) : null;
const recipientId = profileCommentNotificationRecipient(
commenter.id,
parentAuthorId,
agentId
);
if (commentStatus === 'visible' && recipientId) {
const profileLink = agentProfileHref(owner, `profile-comment-${commentId}`);
const isDirectReplyRecipient = Boolean(parentAuthorId && recipientId === parentAuthorId);
await createNotification({
userId: agentId,
userId: recipientId,
category: 'comments',
source: 'agent',
title: 'New profile comment',
body: `${commenter.displayName} posted a comment on your Agent profile.`,
title: parentId ? 'New profile reply' : 'New profile comment',
body: parentId
? isDirectReplyRecipient
? `${commenter.displayName} replied to your message on ${owner.displayName}'s Agent profile.`
: `${commenter.displayName} replied in a conversation on your Agent profile.`
: `${commenter.displayName} posted a comment on your Agent profile.`,
link: profileLink,
actions: [{
label: 'VIEW COMMENT',
label: parentId ? 'VIEW REPLY' : 'VIEW COMMENT',
href: profileLink
}]
});
}
if (commentStatus === 'visible' && parentId) {
await notifyProfileCommentFollowers({
commentId,
authorId: commenter.id,
authorDisplayName: commenter.displayName,
profileOwnerId: agentId,
profileOwnerDisplayName: owner.displayName,
profileOwnerPublicSlug: owner.publicSlug,
directRecipientId: recipientId
});
}
return {
profileCommentSaved: true,
profileCommentReviewQueued: commentStatus === 'pending_review',
profileCommentId: commentId,
profileCommentParentId: parentId,
profileCommentResultToken: randomUUID()
};
},
toggleProfileCommentFollow: async ({ params, cookies, request }) => {
const viewer = await getSessionUser(cookies);
const formData = await request.formData();
const commentId = String(formData.get('commentId') ?? '').trim();
const intent = String(formData.get('intent') ?? '').trim();
const failure = (status: number, message: string) => fail(status, {
profileCommentFollowError: message,
profileCommentFollowId: commentId,
profileCommentFollowResultToken: randomUUID()
});
if (!viewer) return failure(401, 'Sign in to follow a conversation.');
if (!commentId || !['follow', 'unfollow'].includes(intent)) {
return failure(400, 'Choose a conversation and follow preference.');
}
const database = await getDatabase();
const owner = await resolveAgentProfile(database, params.id);
if (!owner) return failure(404, 'Agent profile not found.');
const conversation = await profileCommentConversation(commentId, owner.id);
if (!conversation) return failure(404, 'That conversation is no longer available.');
if (intent === 'follow') {
await (await database.prepare(`INSERT OR IGNORE INTO comment_thread_follows
(user_id,root_comment_id,created_at) VALUES(?,?,?)`))
.run(viewer.id, conversation.rootId, new Date().toISOString());
} else {
await (await database.prepare(
'DELETE FROM comment_thread_follows WHERE user_id=? AND root_comment_id=?'
)).run(viewer.id, conversation.rootId);
}
return {
profileCommentFollowSaved: true,
profileCommentFollowing: intent === 'follow',
profileCommentFollowId: commentId,
profileCommentFollowResultToken: randomUUID()
};
},
deleteProfileComment: async ({ params, cookies, request }) => {
const viewer = await getSessionUser(cookies);
if (!viewer) return fail(401, { profileCommentError: 'Sign in to remove a comment.' });
@@ -320,9 +582,12 @@ export const actions: Actions = {
return fail(403, { profileCommentError: 'You can remove only your own comment.' });
}
const removedAt = new Date().toISOString();
await (await database.prepare(`UPDATE comments SET status='removed',removed_at=?,removed_by=?,
const removed = await (await database.prepare(`UPDATE comments SET status='removed',removed_at=?,removed_by=?,
removal_reason='Removed by author',updated_at=? WHERE id=? AND status='visible'`))
.run(removedAt, viewer.id, removedAt, commentId);
if (!removed.changes) {
return fail(409, { profileCommentError: 'That comment changed before it could be removed.' });
}
return {
profileCommentDeleted: true,
profileCommentId: commentId,