Files
shd.build/src/lib/server/agent-profiles.ts
T

91 lines
3.4 KiB
TypeScript

import type { Database } from '@tursodatabase/database';
import {
displayNameKey,
newAgentDisplayName,
publicProfileSlugBase
} from '$lib/agents';
export async function displayNameIsAvailable(
database: Database,
displayName: string,
exceptUserId = ''
): Promise<boolean> {
const row = await (await database.prepare(
'SELECT user_id FROM user_profiles WHERE display_name_key=? AND user_id<>?'
)).get(displayNameKey(displayName), exceptUserId);
return !row;
}
export async function createAvailableDisplayName(
database: Database,
preferredName: string,
userId: string,
placeholder = false
): Promise<string> {
if (!placeholder && await displayNameIsAvailable(database, preferredName)) return preferredName;
if (placeholder) {
const names = await (await database.prepare(
"SELECT display_name FROM user_profiles WHERE display_name LIKE 'Agent %'"
)).all();
let sequence = names.reduce((highest, row) => {
const match = /^Agent ([1-9]\d*)$/.exec(String(row.display_name));
return match ? Math.max(highest, Number(match[1])) : highest;
}, 0) + 1;
while (!(await displayNameIsAvailable(database, newAgentDisplayName(sequence)))) sequence += 1;
return newAgentDisplayName(sequence);
}
for (let suffixLength = 5; suffixLength <= 12; suffixLength += 1) {
const candidate = `${preferredName} ${userId.replaceAll('-', '').slice(0, suffixLength).toLowerCase()}`;
if (await displayNameIsAvailable(database, candidate)) return candidate;
}
throw new Error('Unable to allocate a unique display name.');
}
export async function createPublicProfileSlug(
database: Database,
displayName: string,
userId: string
): Promise<string> {
const base = publicProfileSlugBase(displayName, userId);
const existing = await (await database.prepare(
'SELECT user_id FROM user_profiles WHERE public_slug=?'
)).get(base);
if (!existing || String(existing.user_id) === userId) return base;
return `${base.slice(0, 55).replace(/-+$/g, '')}~${userId.replaceAll('-', '').slice(0, 8).toLowerCase()}`;
}
export async function insertUserProfile(
database: Database,
userId: string,
preferredName: string,
updatedAt: string,
options: { placeholder?: boolean; resolveCollision?: boolean } = {}
): Promise<{ displayName: string; publicSlug: string }> {
const displayName = options.placeholder || options.resolveCollision
? await createAvailableDisplayName(database, preferredName, userId, Boolean(options.placeholder))
: preferredName;
const publicSlug = await createPublicProfileSlug(database, displayName, userId);
await (await database.prepare(`INSERT INTO user_profiles
(user_id,display_name,display_name_key,public_slug,updated_at) VALUES(?,?,?,?,?)`))
.run(userId, displayName, displayNameKey(displayName), publicSlug, updatedAt);
return { displayName, publicSlug };
}
export async function resolveAgentProfile(
database: Database,
identifier: string
): Promise<{ id: string; publicSlug: string; displayName: string } | null> {
const row = await (await database.prepare(`SELECT users.id,
COALESCE(profile.public_slug,users.id) AS public_slug,
COALESCE(profile.display_name,users.username) AS display_name
FROM users LEFT JOIN user_profiles profile ON profile.user_id=users.id
WHERE users.id=? OR profile.public_slug=?
ORDER BY CASE WHEN profile.public_slug=? THEN 0 ELSE 1 END
LIMIT 1`)).get(identifier, identifier, identifier);
return row ? {
id: String(row.id),
publicSlug: String(row.public_slug),
displayName: String(row.display_name)
} : null;
}