Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kentcdodds/producthunt

src/guide.ts

69 lines · 2.9 KB · TypeScript
import {
	PRODUCTHUNT_ACCESS_TOKEN_SECRET,
	PRODUCTHUNT_APPLICATIONS_URL,
	PRODUCTHUNT_AUTHORIZE_URL,
	PRODUCTHUNT_CLIENT_SECRET_SECRET,
	PRODUCTHUNT_GRAPHQL_URL,
	PRODUCTHUNT_INTEGRATION,
	PRODUCTHUNT_TOKEN_URL,
} from './request.ts'

/** Default Kody deployment origin used when none is provided. */
export const DEFAULT_KODY_ORIGIN = 'https://kody.codes'

/**
 * Builds the hosted Kody OAuth connect URL for the saved `producthunt` integration.
 *
 * Pass your own deployment origin when this package runs somewhere other than
 * the default deployment.
 */
export function getConnectUrl(origin = DEFAULT_KODY_ORIGIN) {
	const params = new URLSearchParams({
		provider: PRODUCTHUNT_INTEGRATION,
		authorizeUrl: PRODUCTHUNT_AUTHORIZE_URL,
		tokenUrl: PRODUCTHUNT_TOKEN_URL,
		apiBaseUrl: PRODUCTHUNT_GRAPHQL_URL,
		allowedHosts: 'api.producthunt.com',
		flow: 'confidential',
		scopes: 'public private',
		dashboardUrl: PRODUCTHUNT_APPLICATIONS_URL,
		providerSetupInstructions:
			'Create a Confidential Product Hunt OAuth app. Register the exact redirect URI shown in the connect wizard (https://kody.codes/connect/oauth on the default deployment). Paste the client ID and client secret into the Kody form. Public clients cannot store the secret Kody needs.',
	})
	return `${origin}/connect/oauth?${params.toString()}`
}

/**
 * Returns setup guidance for agents and humans.
 */
export function getSetupGuide(origin = DEFAULT_KODY_ORIGIN) {
	return {
		integration: PRODUCTHUNT_INTEGRATION,
		connectUrl: getConnectUrl(origin),
		redirectUri: `${origin}/connect/oauth`,
		applicationUrl: PRODUCTHUNT_APPLICATIONS_URL,
		clientType: 'confidential',
		scopes: ['public', 'private'],
		requiredSecrets: [PRODUCTHUNT_ACCESS_TOKEN_SECRET, PRODUCTHUNT_CLIENT_SECRET_SECRET],
		recommendedApprovedHosts: ['api.producthunt.com'],
		notes: [
			'Choose Confidential when creating the Product Hunt app. Kody stores the client secret server-side.',
			'Register the redirect URI exactly as shown. A truncated URI will fail the callback.',
			'Private scope is required for viewer.user. Public is enough for featured posts.',
			`Reconnect any time with ${origin}/connect/oauth?provider=producthunt after the integration is saved.`,
			'Use request() for uncommon GraphQL queries or mutations. Write mutations need Product Hunt approval plus the write scope.',
		],
	}
}

/**
 * Return Product Hunt OAuth setup guidance and connect URL for this deployment.
 * @returns Integration metadata, redirect URI, required secrets, and setup notes.
 * @example
 * import guide from 'kody:@kentcdodds/producthunt/guide'
 * const info = await guide({ origin: 'https://kody.codes' })
 * // => { integration: 'producthunt', connectUrl: 'https://.../connect/oauth?...', requiredSecrets: ['producthuntAccessToken', 'producthuntClientSecret'] }
 */
export default async function guideEntrypoint(params: { origin?: string } = {}) {
	return getSetupGuide(params.origin ?? DEFAULT_KODY_ORIGIN)
}