167 lines · 7.7 KB
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireViewer, viewerMiddleware } from "./middleware";
import { all, batch, first, run, stmt } from "~/server/db";
import { assertBelowCap, CAPS, enforceRateLimit, limitBrowse, limitWrite } from "~/server/limits";
import { canAdministerAccount, canCreateInAccount, requireRepoAccess } from "~/server/repos";
import { accountNameTaken } from "~/server/accounts";
import { profileReadme } from "~/server/profiles";
import { recordActivity } from "~/server/events/emit";
import { repoMoveStatements } from "~/server/redirects";
import { repoOwnershipChangeStatements } from "~/server/integrations/ownership";
import { syncHostingForRepo, syncSitesForOwner } from "~/server/sites/sync";
import { confirmPassword } from "~/server/account-security";
import { badRequest, conflict, forbidden, notFoundError } from "~/server/errors";
import { accountName, repoRef } from "~/lib/validation";

const FOLLOW_PAGE = 100;

async function accountByName(name: string) {
  const account = await first<{ id: string; name: string; kind: "user" | "org"; avatar_key: string | null }>(
    "SELECT id, name, kind, avatar_key FROM accounts WHERE name = ?",
    name,
  );
  if (!account) throw notFoundError("Account not found");
  return account;
}

export const getProfileExtras = createServerFn({ method: "GET" })
  .middleware([viewerMiddleware])
  .validator(z.object({ name: z.string().min(1).max(64) }))
  .handler(async ({ data, context }) => {
    await limitBrowse(context.viewer);
    const account = await accountByName(data.name);
    const counts = await first<{ followers: number; following: number }>(
      `SELECT (SELECT COUNT(*) FROM follows WHERE followee_id = ?1) AS followers,
              (SELECT COUNT(*) FROM follows WHERE follower_id = ?1) AS following`,
      account.id,
    );
    const isFollowing = context.viewer
      ? (await first(