NobGit
public anord read

Ledger

Why work hard when you can work easier?

Languages

Repository composition by tracked source files.

TypeScript
TypeScript 86% CSS 10% SQL 3% Shell 1% HTML 0%
Create file Wiki Documentation
Clone
https://nobgit.com/orgs/anord/ledger.git
ssh://[email protected]:2222/orgs/anord/ledger.git
setup.ts
import { hashPassword, createSessionToken, getUserForSession } from "./auth.js";
import { logAudit } from "./audit.js";
import { pool } from "../db/pool.js";

const FIXED_FOOTER = "Powered by Ledger made by ANord.cc";

interface SetupInput {
  siteName: string;
  brandColor: string;
  publicKnowledgeBaseEnabled: boolean;
  ownerEmail: string;
  ownerDisplayName: string;
  password: string;
}

export async function getSetupStatus() {
  const users = await pool.query(`SELECT COUNT(*)::int AS count FROM users`);
  const branding = await pool.query(`SELECT site_name, brand_color FROM branding_settings ORDER BY created_at ASC LIMIT 1`);

  return {
    isInitialized: users.rows[0].count > 0,
    branding: branding.rows[0] ?? null
  };
}

export async function initializeLedger(input: SetupInput) {
  const existingUsers = await pool.query(`SELECT COUNT(*)::int AS count FROM users`);
  if (existingUsers.rows[0].count > 0) {
    throw new Error("Ledger has already been initialized");
  }

  await pool.query("BEGIN");
  try {
    const ownerRole = await pool.query(`SELECT id FROM roles WHERE key = 'owner'`);

    const user = await pool.query(
      `
        INSERT INTO users (email, password_hash, display_name, primary_role_id)
        VALUES ($1, $2, $3, $4)
        RETURNING id
      `,
      [input.ownerEmail, await hashPassword(input.password), input.ownerDisplayName, ownerRole.rows[0].id]
    );

    const ownerUserId = user.rows[0].id;

    const branding = await pool.query(
      `
        UPDATE branding_settings
        SET site_name = $1, brand_color = $2, footer_text = $3,
            public_knowledge_base_enabled = $4, footer_links = '[]'::jsonb, updated_at = now()
        WHERE id = (SELECT id FROM branding_settings ORDER BY created_at ASC LIMIT 1)
        RETURNING id
      `,
      [input.siteName, input.brandColor, FIXED_FOOTER, input.publicKnowledgeBaseEnabled]
    );

    await pool.query(
      `
        INSERT INTO spaces (name, key, description, visibility, created_by_user_id)
        VALUES
          ('Docs', 'docs', 'Public documentation for your organization', 'public', $1),
          ('Team', 'team', 'Internal knowledge for your organization', 'internal', $1)
        ON CONFLICT (key) DO NOTHING
      `,
      [ownerUserId]
    );

    const docsSpace = await pool.query(`SELECT id FROM spaces WHERE key = 'docs'`);
    const welcomePage = await pool.query(
      `
        INSERT INTO pages (space_id, title, slug, excerpt, visibility, state, is_public, owner_user_id)
        VALUES ($1, 'Welcome to Ledger', 'welcome-to-ledger', 'Start customizing your new knowledge base.', 'public', 'published', true, $2)
        RETURNING id
      `,
      [docsSpace.rows[0].id, ownerUserId]
    );

    const welcomeRevision = await pool.query(
      `
        INSERT INTO page_revisions (page_id, title, slug, excerpt, visibility, state, body_markdown, edited_by_user_id)
        VALUES (
          $1,
          'Welcome to Ledger',
          'welcome-to-ledger',
          'Start customizing your new knowledge base.',
          'public',
          'published',
          '# Welcome to Ledger\n\nYour knowledge base is ready.\n\n## Next steps\n\n- Customize branding\n- Invite your team\n- Publish your first public and internal pages',
          $2
        )
        RETURNING id
      `,
      [welcomePage.rows[0].id, ownerUserId]
    );

    await pool.query(`UPDATE pages SET current_revision_id = $2 WHERE id = $1`, [
      welcomePage.rows[0].id,
      welcomeRevision.rows[0].id
    ]);

    await pool.query("COMMIT");

    const sessionUser = await getUserForSession(ownerUserId);
    const token = createSessionToken(sessionUser!);
    await logAudit(ownerUserId, "setup.initialize", "ledger", "instance", {
      brandingSettingsId: branding.rows[0].id
    });

    return { token, user: sessionUser };
  } catch (error) {
    await pool.query("ROLLBACK");
    throw error;
  }
}