Skip to content

Commit

Permalink
refactor: re-organize and name the username module functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Dec 3, 2024
1 parent cbfd50d commit 63be355
Show file tree
Hide file tree
Showing 15 changed files with 69 additions and 76 deletions.
15 changes: 5 additions & 10 deletions src/lib/leaf/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import { BorshSchema, Component, intoPathSegment } from 'leaf-proto';
import { CommonMark, Description, RawImage, Name } from 'leaf-proto/components';
import _ from 'underscore';

import {
listUsers,
userNameByRauthyId,
userSubspaceByRauthyId,
userSubspaceByUsername
} from '$lib/usernames/index';
import { usernames } from '$lib/usernames/index';
import { LinkVerifier } from '$lib/link_verifier/LinkVerifier';
import { resolveUserSubspaceFromDNS } from '$lib/dns/resolve';
import { leafClient, subspace_link } from '.';
Expand Down Expand Up @@ -200,11 +195,11 @@ export function appendSubpath(link: ExactLink, ...pathSegments: IntoPathSegment[
}

export async function profileLinkById(rauthyId: string): Promise<ExactLink> {
const subspace = await userSubspaceByRauthyId(rauthyId);
const subspace = await usernames.subspaceByRauthyId(rauthyId);
return subspace_link(subspace, null);
}
export async function profileLinkByUsername(username: string): Promise<ExactLink | undefined> {
const subspace = await userSubspaceByUsername(username);
const subspace = await usernames.getSubspace(username);
if (!subspace) return;
return subspace_link(subspace, null);
}
Expand Down Expand Up @@ -308,7 +303,7 @@ export async function getProfileByDomain(domain: string): Promise<Profile | unde
export async function setProfileById(rauthyId: string, profile: Profile): Promise<void> {
let link = await profileLinkById(rauthyId);
if (!link) throw `user has not yet claimed a username.`;
const userName = await userNameByRauthyId(rauthyId);
const userName = await usernames.getByRauthyId(rauthyId);
if (!userName) throw `user has no username`;
const linkVerifier = new LinkVerifier(profile.links, userName);
await linkVerifier.verify();
Expand All @@ -319,7 +314,7 @@ export async function getProfiles(): Promise<
{ link: ExactLink; profile: Profile; username?: string }[]
> {
const profiles: { link: ExactLink; profile: Profile; username?: string }[] = [];
for await (const user of listUsers()) {
for await (const user of usernames.list()) {
const link = subspace_link(user.subspace, null);
const profile = await getProfile(link);
if (!profile) continue;
Expand Down
42 changes: 27 additions & 15 deletions src/lib/usernames/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,23 @@ import { APP_IPS } from '../dns/server';
import { validDomainRegex, validUsernameRegex } from './client';
import { dev } from '$app/environment';

export { validUsernameRegex, validDomainRegex };

const USER_NAMES_PREFIX = 'weird:users:names:';
const USER_RAUTHY_IDS_PREFIX = 'weird:users:rauthyIds:';
const USER_SUBSPACES_PREFIX = 'weird:users:subspaces:';

export async function setUserSubspace(rauthyId: string, subspace: SubspaceId) {
async function setSubspace(rauthyId: string, subspace: SubspaceId) {
const sspace = base32Encode(subspace);
await redis.hSet(USER_RAUTHY_IDS_PREFIX + rauthyId, 'subspace', sspace);
await redis.hSet(USER_SUBSPACES_PREFIX + sspace, 'rauthyId', rauthyId);
}

export async function claimUsername(
async function claim(
input: { username: string } | { domain: string; skipDomainCheck?: boolean },
rauthyId: string
) {
const rauthyIdKey = USER_RAUTHY_IDS_PREFIX + rauthyId;

const subspace = base32Encode(await userSubspaceByRauthyId(rauthyId));
const subspace = base32Encode(await subspaceByRauthyId(rauthyId));
const subspaceKey = USER_SUBSPACES_PREFIX + subspace;

let username;
Expand Down Expand Up @@ -140,7 +138,7 @@ with value "${expectedValue}". Found other values: ${txtRecords.map((v) => `"${v
throw 'Could not claim username after 3 attempts.';
}

export async function unsetUsername(username: string) {
async function unset(username: string) {
const usernameKey = USER_NAMES_PREFIX + username;

await redis.watch(usernameKey);
Expand All @@ -164,7 +162,7 @@ export async function unsetUsername(username: string) {
await multi.exec();
}

export async function* listUsers(): AsyncGenerator<{
async function* list(): AsyncGenerator<{
username?: string;
rauthyId: string;
subspace: Uint8Array;
Expand All @@ -174,41 +172,55 @@ export async function* listUsers(): AsyncGenerator<{
const rauthyId = segments[segments.length - 1];
yield {
rauthyId,
username: await userNameByRauthyId(rauthyId),
subspace: await userSubspaceByRauthyId(rauthyId)
username: await getByRauthyId(rauthyId),
subspace: await subspaceByRauthyId(rauthyId)
};
}
}

export async function userNameByRauthyId(rauthyId: string): Promise<string | undefined> {
async function getByRauthyId(rauthyId: string): Promise<string | undefined> {
return await redis.hGet(USER_RAUTHY_IDS_PREFIX + rauthyId, 'username');
}

export async function userSubspaceByRauthyId(rauthyId: string): Promise<Uint8Array> {
async function subspaceByRauthyId(rauthyId: string): Promise<Uint8Array> {
let subspaceStr = await redis.hGet(USER_RAUTHY_IDS_PREFIX + rauthyId, 'subspace');
let subspace;
if (!subspaceStr) {
subspace = await leafClient.create_subspace();
await setUserSubspace(rauthyId, subspace);
await setSubspace(rauthyId, subspace);
return subspace;
} else {
return base32Decode(subspaceStr);
}
}

export async function userSubspaceByUsername(username: string): Promise<Uint8Array | undefined> {
async function getSubspace(username: string): Promise<Uint8Array | undefined> {
const subspaceStr = await redis.hGet(USER_NAMES_PREFIX + username, 'subspace');
return subspaceStr ? base32Decode(subspaceStr) : undefined;
}
export async function userRauthyIdByUsername(username: string): Promise<string | undefined> {
async function getRauthyId(username: string): Promise<string | undefined> {
return await redis.hGet(USER_NAMES_PREFIX + username, 'rauthyId');
}

export async function userNameAndIdBySubspace(
async function getBySubspace(
subspace: SubspaceId
): Promise<{ username?: string; rauthyId?: string }> {
const key = USER_SUBSPACES_PREFIX + base32Encode(subspace);
const username = await redis.hGet(key, 'username');
const rauthyId = await redis.hGet(key, 'rauthyId');
return { username, rauthyId };
}

export const usernames = {
validDomainRegex,
validUsernameRegex,
setSubspace,
claim,
unset,
list,
getByRauthyId,
subspaceByRauthyId,
getSubspace,
getRauthyId,
getBySubspace
};
6 changes: 3 additions & 3 deletions src/routes/(app)/[username]/+layout.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getSession } from '$lib/rauthy/server';
import { leafClient, subspace_link } from '$lib/leaf';
import { Name } from 'leaf-proto/components';
import { env } from '$env/dynamic/public';
import { userRauthyIdByUsername, userSubspaceByUsername } from '$lib/usernames/index';
import { usernames } from '$lib/usernames/index';
import { base32Encode } from 'leaf-proto';

export const load: LayoutServerLoad = async ({ fetch, params, request }) => {
Expand All @@ -19,13 +19,13 @@ export const load: LayoutServerLoad = async ({ fetch, params, request }) => {

let profileMatchesUserSession = false;

const subspace = await userSubspaceByUsername(fullUsername);
const subspace = await usernames.getSubspace(fullUsername);
if (!subspace) return error(404, `User not found: ${fullUsername}`);

const profile = (await getProfile(subspace_link(subspace, null))) || { tags: [], links: [] };

const { sessionInfo } = await getSession(fetch, request);
if (sessionInfo && sessionInfo.user_id == (await userRauthyIdByUsername(fullUsername))) {
if (sessionInfo && sessionInfo.user_id == (await usernames.getRauthyId(fullUsername))) {
profileMatchesUserSession = true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/routes/(app)/[username]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@
</div>
<input type="url" placeholder="Enter RSS Link" name="rssLink" />

<button type="submit" class="text-base variant-filled-secondary">Import</button>
<button type="submit" class="variant-filled-secondary text-base">Import</button>
</div>
<span class="text-sm text-error-300">{form?.rss?.error}</span>
</form>
Expand Down
4 changes: 2 additions & 2 deletions src/routes/(app)/[username]/[slug]/revisions/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { PageServerLoad } from './$types';
import { error, redirect } from '@sveltejs/kit';
import { env } from '$env/dynamic/public';
import { leafClient, subspace_link } from '$lib/leaf';
import { userSubspaceByUsername } from '$lib/usernames/index';
import { usernames } from '$lib/usernames/index';

export const load: PageServerLoad = async ({
params
Expand All @@ -14,7 +14,7 @@ export const load: PageServerLoad = async ({
const fullUsername = params.username!.includes('.')
? params.username!
: `${params.username}.${env.PUBLIC_USER_DOMAIN_PARENT}`;
const subspace = await userSubspaceByUsername(fullUsername);
const subspace = await usernames.getSubspace(fullUsername);
if (!subspace) return error(404, `User not found: ${fullUsername}`);

if (!subspace) return error(404, `User not found: ${fullUsername}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { env } from '$env/dynamic/public';
import { leafClient, subspace_link } from '$lib/leaf';
import { CommonMark, Name } from 'leaf-proto/components';
import { Page } from '../../../types';
import { userNameByRauthyId, userSubspaceByUsername } from '$lib/usernames/index';
import { usernames } from '$lib/usernames/index';

export const load: PageServerLoad = async ({
params
Expand All @@ -26,7 +26,7 @@ export const load: PageServerLoad = async ({
? params.username!
: `${params.username}.${env.PUBLIC_USER_DOMAIN_PARENT}`;

const subspace = await userSubspaceByUsername(fullUsername);
const subspace = await usernames.getSubspace(fullUsername);
if (!subspace) return error(404, `User not found: ${fullUsername}`);
const revisionLink = subspace_link(subspace, params.slug, { Uint: BigInt(params.revision) });

Expand All @@ -52,7 +52,7 @@ export const load: PageServerLoad = async ({
const revisionAuthor =
(await (async () => {
if (!authorId) return;
return await userNameByRauthyId(authorId);
return await usernames.getByRauthyId(authorId);
})()) || '';

return {
Expand Down
6 changes: 2 additions & 4 deletions src/routes/(app)/[username]/new/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import { ensureUsernameMatchesSessionUserId } from '../utils';
import {
WebLinks,
WeirdWikiPage,
appendSubpath,
getProfileById,
profileLinkById
} from '$lib/leaf/profile';
import { Page } from '../types';
import { leafClient, subspace_link } from '$lib/leaf';
import { CommonMark, Name } from 'leaf-proto/components';
import { userSubspaceByRauthyId } from '$lib/usernames/index';
import { usernames } from '$lib/usernames/index';

export const load: PageServerLoad = async ({ fetch, params, request, url }) => {
const { sessionInfo } = await getSession(fetch, request);
Expand Down Expand Up @@ -41,7 +39,7 @@ export const actions = {
return error(400, `Error parsing form data: ${e}`);
}

const subspace = await userSubspaceByRauthyId(sessionInfo.user_id);
const subspace = await usernames.subspaceByRauthyId(sessionInfo.user_id);
const pageLink = subspace_link(subspace, data.slug);

const ent = await leafClient.get_components(pageLink);
Expand Down
18 changes: 6 additions & 12 deletions src/routes/(app)/[username]/settings/handle/+server.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import { getSession } from '$lib/rauthy/server';
import {
claimUsername,
unsetUsername,
userNameByRauthyId,
validUsernameRegex,
validDomainRegex
} from '$lib/usernames';
import { usernames } from '$lib/usernames';
import { type RequestHandler, json } from '@sveltejs/kit';
import { z } from 'zod';

const Req = z.union([
z.object({
username: z.string().regex(validUsernameRegex)
username: z.string().regex(usernames.validUsernameRegex)
}),

z.object({
domain: z.string().regex(validDomainRegex)
domain: z.string().regex(usernames.validDomainRegex)
})
]);
type Req = z.infer<typeof Req>;
Expand All @@ -31,12 +25,12 @@ export const POST: RequestHandler = async ({ request, fetch }) => {
if (!sessionInfo) {
return new Response(null, { status: 403 });
}
const oldUsername = await userNameByRauthyId(sessionInfo.user_id);
const oldUsername = await usernames.getByRauthyId(sessionInfo.user_id);

try {
await claimUsername(parsed.data, sessionInfo.user_id);
await usernames.claim(parsed.data, sessionInfo.user_id);
if (oldUsername) {
await unsetUsername(oldUsername);
await usernames.unset(oldUsername);
}
return json(parsed.data);
} catch (e) {
Expand Down
5 changes: 2 additions & 3 deletions src/routes/(app)/[username]/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { env } from '$env/dynamic/public';
import { profileLinkByUsername } from '$lib/leaf/profile';
import { userRauthyIdByUsername } from '$lib/usernames/index';
import { usernames } from '$lib/usernames/index';
import { error } from '@sveltejs/kit';

export async function ensureUsernameMatchesSessionUserId(
Expand All @@ -10,7 +9,7 @@ export async function ensureUsernameMatchesSessionUserId(
const fullUsername = username.includes('.')
? username
: `${username}.${env.PUBLIC_USER_DOMAIN_PARENT}`;
const id = await userRauthyIdByUsername(fullUsername);
const id = await usernames.getRauthyId(fullUsername);

if (userId != id) {
return error(403, 'Unauthorized');
Expand Down
6 changes: 3 additions & 3 deletions src/routes/(app)/claim-username/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getSession } from '$lib/rauthy/server';
import { fail, redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types.js';
import { claimUsername, userNameByRauthyId } from '$lib/usernames/index';
import { usernames } from '$lib/usernames/index';
import { getAvatar, profileLinkById, setAvatar } from '$lib/leaf/profile.js';
import { RawImage } from 'leaf-proto/components.js';

Expand All @@ -11,7 +11,7 @@ import { glass } from '@dicebear/collection';
export const load: PageServerLoad = async ({ fetch, request }) => {
const { sessionInfo } = await getSession(fetch, request);
if (!sessionInfo) return redirect(303, '/login');
const username = await userNameByRauthyId(sessionInfo.user_id);
const username = await usernames.getByRauthyId(sessionInfo.user_id);
if (username) {
return redirect(303, `/${username}`);
}
Expand All @@ -27,7 +27,7 @@ export const actions = {
if (!username) return fail(400, { error: 'Username not provided ' });

try {
await claimUsername({ username }, sessionInfo.user_id);
await usernames.claim({ username }, sessionInfo.user_id);
const profileLink = await profileLinkById(sessionInfo.user_id);
const avatar = createAvatar(glass, { seed: sessionInfo.user_id, radius: 50 });
if (!(await getAvatar(profileLink))) {
Expand Down
4 changes: 2 additions & 2 deletions src/routes/(app)/my-profile/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { getSession } from '$lib/rauthy/server';
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { userNameByRauthyId as usernameByRauthyId } from '$lib/usernames/index';
import { usernames } from '$lib/usernames/index';
import { env } from '$env/dynamic/public';

export const load: PageServerLoad = async ({ fetch, request }): Promise<void> => {
const { sessionInfo } = await getSession(fetch, request);
if (!sessionInfo) return redirect(303, '/login');
const username = await usernameByRauthyId(sessionInfo.user_id);
const username = await usernames.getByRauthyId(sessionInfo.user_id);
if (!username) return redirect(303, '/claim-username');
return redirect(303, `/${username.split('.' + env.PUBLIC_USER_DOMAIN_PARENT)[0]}`);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '$lib/leaf/profile';
import { CommonMark, Description, Name } from 'leaf-proto/components';
import { getSession } from '$lib/rauthy/server';
import { userNameAndIdBySubspace } from '$lib/usernames/index';
import { usernames } from '$lib/usernames/index';

type Data =
| {
Expand All @@ -34,7 +34,7 @@ export const load = async ({ params }: Parameters<PageServerLoad>[0]): Promise<D
Subspaces: await Promise.all(
subspaces.map(async (x) => ({
subspace: base32Encode(x),
...(await userNameAndIdBySubspace(x))
...(await usernames.getBySubspace(x))
}))
),
namespace: params.namespace
Expand Down
Loading

0 comments on commit 63be355

Please sign in to comment.