tweakcn Documentation

repository·main·Indexed 27 days ago

https://github.com/jnsahaj/tweakcn

A visual theme editor for Tailwind CSS and shadcn/ui components that allows developers to customize UI components using presets or advanced settings. Features include an OAuth 2.0 API for theme and profile management, community theme publishing, and a set of React hooks for theme mutations and data retrieval.

Tokens
3.7K
Snippets
7
Records
28
Agent score
95%

What's inside tweakcn

  1. Implement the OAuth 2.0 Authorization Code flow

    main

    tweakcn uses the standard OAuth 2.0 Authorization Code flow. PKCE is supported for public clients (SPAs, mobile apps) by adding code_challenge and code_challenge_method=S256 to the authorization request, and providing the code_verifier during the token exchange.

    1. Redirect the user to authorize

    Send the user to: GET https://tweakcn.com/api/oauth/authorize?client_id=CLIENT_ID&redirect_uri=https://myapp.com/callback&response_type=code&scope=themes:read profile:read&state=RANDOM_STRING

    Upon successful sign-in, the user is redirected to your redirect_uri with a code parameter.

    2. Exchange the code for tokens

    Exchange the authorization code for an access token and refresh token using a POST request:

    3. Call the API

    Use the access_token as a Bearer token in the Authorization header.

    4. Refresh tokens

    When the access token expires (typically after 1 hour), use the refresh_token to obtain a new pair.

    5. Revoke tokens

    Revoke an access or refresh token when it is no longer needed.

    # 1. Exchange code for tokens
    curl -X POST https://tweakcn.com/api/oauth/token \
      -d grant_type=authorization_code \
      -d client_id=CLIENT_ID \
      -d client_secret=CLIENT_SECRET \
      -d code=AUTH_CODE \
      -d redirect_uri=https://myapp.com/callback
    
    # 2. Call the API
    curl https://tweakcn.com/api/v1/themes \
      -H "Authorization: Bearer ACCESS_TOKEN"
    
    # 3. Refresh tokens
    curl -X POST https://tweakcn.com/api/oauth/token \
      -d grant_type=refresh_token \
      -d client_id=CLIENT_ID \
      -d client_secret=CLIENT_SECRET \
      -d refresh_token=REFRESH_TOKEN
    
    # 4. Revoke tokens
    curl -X POST https://tweakcn.com/api/oauth/revoke \
      -d token=ACCESS_OR_REFRESH_TOKEN
  2. Register an OAuth app via CLI

    main

    To allow external applications to authenticate users and access data, register an OAuth app using the provided CLI script. This will generate a client_id and a client_secret. Note that the client_secret is only displayed once during creation.

    npx tsx scripts/create-oauth-app.ts \
      --name "My App" \
      --redirect-uris "https://myapp.com/callback" \
      --scopes "themes:read,profile:read" \
      --description "Optional description"
  3. Run the application locally using Docker Compose

    main

    To run the full stack (application and database) locally, use the provided docker-compose.yml configuration. The setup builds the application from the local directory, maps port 3000 for the web service, and uses a PostgreSQL 15 database.

    Service Details:

    • app: Builds from the current directory, uses .env.local for environment variables, and mounts the current directory to /app for live development. It automatically runs npx drizzle-kit push to sync the database schema before starting the development server via npm run dev.
    • db: A PostgreSQL 15 service running on port 5432 with default credentials (postgres/postgres) and a database named tweakcn.
  4. Integrate tweakcn with Better Auth genericOAuth

    main

    tweakcn can be used as a provider within the Better Auth genericOAuth plugin. You must configure the authorizationUrl, tokenUrl, and userInfoUrl correctly on the server, and use genericOAuthClient on the client side.

    // server
    import { genericOAuth } from "better-auth/plugins";
    
    export const auth = betterAuth({
      plugins: [
        genericOAuth({
          config: [
            {
              providerId: "tweakcn",
              clientId: process.env.TWEAKCN_CLIENT_ID,
              clientSecret: process.env.TWEAKCN_CLIENT_SECRET,
              authorizationUrl: "https://tweakcn.com/api/oauth/authorize",
              tokenUrl: "https://tweakcn.com/api/oauth/token",
              userInfoUrl: "https://tweakcn.com/api/oauth/userinfo",
              scopes: ["themes:read", "profile:read"],
            },
          ],
        }),
      ],
    });
    
    // client
    import { genericOAuthClient } from "better-auth/client/plugins";
    
    const authClient = createAuthClient({
      plugins: [genericOAuthClient()],
    });
    
    await authClient.signIn.oauth2({
      providerId: "tweakcn",
      callbackURL: "/dashboard",
    });
  5. Configure Drizzle ORM with drizzle-kit

    main

    The project uses drizzle-kit for database schema management and migrations. The configuration is defined using defineConfig and requires a schema file, an output directory for migrations, and database credentials. Environment variables are loaded from .env.local using dotenv.

    import "dotenv/config";
    import { defineConfig } from "drizzle-kit";
    import { config } from "dotenv";
    
    config({ path: ".env.local" });
    
    export default defineConfig({
      out: "./drizzle",
      schema: "./db/schema.ts",
      dialect: "postgresql",
      dbCredentials: {
        url: process.env.DATABASE_URL!,
      },
    });
  6. Reference: tweakcn OAuth API Endpoints

    main

    All API endpoints require an Authorization: Bearer <access_token> header.

    • GET /api/oauth/userinfo: OIDC-compatible endpoint returning flat user fields. Requires profile:read scope.
    • GET /api/v1/me: Returns the authenticated user's profile. Requires profile:read scope.
    • GET /api/v1/themes: Returns all themes owned by the authenticated user. Requires themes:read scope.
    • GET /api/v1/themes/:themeId: Returns a single theme by ID (must be owned by the user). Requires themes:read scope.
  7. Wait for a font to load with `waitForFont`

    main
    Asynchronously waits for a specific font family and weight to load using the native document.fonts.load API. It includes a configurable timeout to prevent infinite waiting. Returns a Promise<boolean> which resolves to true if the font is loaded, or false if it fails or times out.
  8. Extract a font family name from a CSS string with `extractFontFamily`

    main
    Parse a CSS font-family string to retrieve only the primary font name. The function strips quotes, handles whitespace, and returns null if the extracted name is a known system font or if the input is invalid.