mirror your GitHub repos to tangled.org automatically
1

Configure Feed

Select the types of activity you want to include in your feed.

1import { installationAccountLogin, userAdministersInstallation, userOctokitFromCode } from '#server/utils/github-app' 2import { markInstallOwned } from '#server/utils/install-ownership' 3import { sessionConfig } from '#server/utils/server-session' 4 5interface OAuthFlowData { 6 state: string 7 installationId: number 8} 9 10/** 11 * GitHub user-OAuth callback. Verifies the `state` against the sealed flow 12 * cookie, exchanges the code for a user token, and confirms the user 13 * administers the installation. On success, marks the install owned (a sealed 14 * cookie the atproto callback checks before binding) and sends the user into 15 * the tangled connect flow. 16 */ 17export default defineEventHandler(async event => { 18 const query = getQuery(event) 19 const code = typeof query.code === 'string' ? query.code : null 20 const state = typeof query.state === 'string' ? query.state : null 21 if (!code || !state) { 22 throw createError({ statusCode: 400, statusMessage: 'missing code or state' }) 23 } 24 25 const flow = await useSession<OAuthFlowData>(event, { 26 ...sessionConfig(), 27 name: 'synchub-gh-oauth', 28 maxAge: 10 * 60, 29 }) 30 const expectedState = flow.data.state 31 const installationId = flow.data.installationId 32 await flow.clear() 33 34 if (!expectedState || expectedState !== state || typeof installationId !== 'number') { 35 throw createError({ statusCode: 400, statusMessage: 'invalid or expired oauth state' }) 36 } 37 38 const userOctokit = await userOctokitFromCode(code) 39 const administers = await userAdministersInstallation(userOctokit, installationId) 40 if (!administers) { 41 throw createError({ 42 statusCode: 403, 43 statusMessage: 'your GitHub account does not administer this installation', 44 }) 45 } 46 47 await markInstallOwned(event, installationId) 48 49 // Ownership proven. Hand off to the connect page, which now lets the user 50 // pick the tangled handle to bind. Carry the account login for display. 51 const login = await installationAccountLogin(installationId) 52 const params = new URLSearchParams({ installation_id: String(installationId), verified: '1' }) 53 if (login) params.set('login', login) 54 await sendRedirect(event, `/connect?${params.toString()}`, 302) 55})