fluidtrends/carmel.sdk

View on GitHub
src/auth/VercelProvider.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { Strategy } from 'passport-github2'
import passport from 'passport'
import OAuth2Strategy from 'passport-oauth2'
import { createDeployment } from '@vercel/client'
import axios from 'axios'

import {
  Authenticator,
  IAuthKey,
  IRepo,
  IAuthProvider,
  AccessTokenType,
} from '..'
import { ICode } from '../types'

/**
 *
 */
export class VercelProvider implements IAuthProvider {
  /** */
  public static APP_CLIENT_ID = 'oac_6cgAIFisZuUgjKYVdM4rdg8s'

  /** */
  public static APP_CLIENT_SECRET = '5CptA1wt73OmfR03VkYbexwK'

  /** @internal */
  protected _authenticator: Authenticator

  /** @internal */
  protected _keys: IAuthKey[]

  /**
   *
   * @param authenticator
   */
  constructor(authenticator: Authenticator) {
    this._authenticator = authenticator
    this._keys = []
  }

  /**
   *
   */
  get keys() {
    return this._keys
  }

  /**
   *
   */
  get token() {
    return this.authenticator.session.token(AccessTokenType.VERCEL)
  }

  /**
   *
   */
  get authenticator() {
    return this._authenticator
  }

  /**
   *
   */
  get isLoggedIn() {
    return this.token !== undefined
  }

  /**
   *
   */
  async setupNewKey(title: string, service?: any) {
    // Looks like we have no github keys stored, let's create one
    const newLocalKey = await this.authenticator.session.keystore.addNewKey(
      'vercel'
    )

    // Keep track of it
    this._keys.push(newLocalKey)

    return newLocalKey
  }

  /**
   *
   * @param service
   */
  async fetchRemoteKeys(service?: any) {
    return []
  }

  /**
   *
   */
  async prepareKeys() {
    if (!this.authenticator.session.isLoggedIn) {
      return
    }

    const { keystore, system } = this.authenticator.session

    // Let's look up local and remote keys
    const localKeys: IAuthKey[] = keystore.keys.get('vercel') || []

    if (localKeys.length === 0) {
      await this.setupNewKey(``)
      return
    }

    localKeys.map((k) => this.keys.push(k))
  }

  /**
   *
   * @param name
   * @param repo
   */
  async push(name: string, repo: IRepo) {
    if (!this.isLoggedIn) return

    let project = await this.request(`v1/projects/ensure-project`, {
      name,
    })

    project = await this.request(
      `v2/projects/${name}`,
      {
        publicSource: true,
        framework: null,
        buildCommand: null,
        devCommand: null,
        outputDirectory: '.web',
        rootDirectory: null,
      },
      true
    )

    if (!project) return

    const deploymentGenerator = () =>
      createDeployment(
        {
          token: this.token!,
          path: repo.dir!.path!,
        },
        {
          name,
          version: 2,
        }
      )

    let deployment
    for await (const event of deploymentGenerator()) {
      switch (event.type) {
        case 'error':
          console.log(event.payload)
          break
        case 'ready':
          deployment = event.payload
          break
      }
    }

    return deployment
  }

  /**
   *
   * @param uri
   * @param options
   * @param patch
   */
  async request(uri: string, options?: any, patch?: boolean) {
    if (!this.isLoggedIn) return

    try {
      const url = `https://api.vercel.com/${uri}`
      const header = {
        headers: {
          Authorization: `Bearer ${this.token}`,
          'Content-Type': 'application/json',
        },
      }

      const { data } = await (options
        ? patch
          ? axios.patch(url, options || {}, header)
          : axios.post(url, options || {}, header)
        : axios.get(url, header))

      return data
    } catch (e) {
      console.log(e)
    }
  }

  /**
   *
   */
  async initialize() {
    passport.use(
      new OAuth2Strategy(
        {
          authorizationURL: 'https://vercel.com/oauth/authorize',
          tokenURL: 'https://api.vercel.com/v2/oauth/access_token',
          clientID: VercelProvider.APP_CLIENT_ID,
          clientSecret: VercelProvider.APP_CLIENT_SECRET,
          callbackURL: `${this.authenticator.baseUrl}/auth/vercel/callback`,
        },
        (accessToken: any, refreshToken: any, profile: any, done: any) => {
          process.nextTick(() => {
            const { user } = this.authenticator
            user?.tokens.push({
              type: AccessTokenType.VERCEL,
              value: accessToken,
            })
            done(null, user)
          })
        }
      )
    )

    this.authenticator.app.get('/auth/vercel', passport.authenticate('oauth2'))

    this.authenticator.app.get(
      '/auth/vercel/callback',
      passport.authenticate('oauth2', { failureRedirect: '/login' }),
      (req, res) => {
        res.redirect('/')
      }
    )
  }
}