91 lines
3.4 KiB
TypeScript
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;
|
|
}
|