Path: blob/main/src/publish/quarto-pub/quarto-pub.ts
6455 views
/*1* quarto-pub.ts2*3* Copyright (C) 2020-2022 Posit Software, PBC4*/56import { AccessToken, Ticket } from "./api/types.ts";7import {8AccountToken,9AccountTokenType,10PublishFiles,11PublishProvider,12} from "../provider-types.ts";13import {14AuthorizationHandler,15authorizeAccessToken,16readAccessTokens,17writeAccessTokens,18} from "../common/account.ts";19import { handlePublish, PublishHandler } from "../common/publish.ts";20import { ApiError, PublishOptions, PublishRecord } from "../types.ts";2122import { QuartoPubClient } from "./api/index.ts";23import { authorizePrompt } from "../account.ts";24import { quartoConfig } from "../../core/quarto.ts";25import { RenderFlags } from "../../command/render/types.ts";2627export const kQuartoPub = "quarto-pub";28export const kQuartoPubAuthTokenVar = "QUARTO_PUB_AUTH_TOKEN";2930export const quartoPubProvider: PublishProvider = {31name: kQuartoPub,32description: "Quarto Pub",33requiresServer: false,34listOriginOnly: false,35accountTokens,36authorizeToken,37removeToken,38resolveTarget,39publish,40isUnauthorized,41isNotFound,42};4344function accountTokens() {45const envTk = environmentAuthToken();46const accessTks = readAccessTokens<AccessToken>(quartoPubProvider.name);4748const accounts: AccountToken[] = [];49if (envTk) {50accounts.push({51type: AccountTokenType.Environment,52name: kQuartoPubAuthTokenVar,53server: null,54token: envTk,55});56}5758if (accessTks) {59for (const accessTk of accessTks) {60accounts.push({61type: AccountTokenType.Authorized,62name: accessTk.email!,63server: null,64token: accessTk.application_token,65});66}67}6869return Promise.resolve(accounts);70}7172async function authorizeToken(_options: PublishOptions) {73if (await authorizePrompt(quartoPubProvider)) {74const token = await authorizeQuartoPubAccessToken();75if (token) {76return {77type: AccountTokenType.Authorized,78name: token.email!,79server: null,80token: token.application_token,81};82} else {83return undefined;84}85}86}8788function removeToken(token: AccountToken) {89writeAccessTokens(90quartoPubProvider.name,91readAccessTokens<AccessToken>(quartoPubProvider.name)?.filter(92(accessToken) => {93return accessToken.email !== token.name;94},95) || [],96);97}9899// Load the .env configuration and the environment.100const dotenvConfig = await quartoConfig.dotenv();101const quartoPubEnvironment = dotenvConfig["QUARTO_PUB_ENVIRONMENT"];102103function environmentAuthToken() {104return Deno.env.get(kQuartoPubAuthTokenVar);105}106107function authorizeQuartoPubAccessToken(): Promise<108AccessToken | undefined109> {110// Create an unauthorized QuartoPubClient.111const client = new QuartoPubClient(quartoPubEnvironment);112113const provider: AuthorizationHandler<AccessToken, Ticket> = {114name: kQuartoPub,115116createTicket: (): Promise<Ticket> =>117client.createTicket(dotenvConfig["QUARTO_PUB_APP_CLIENT_ID"]),118119authorizationUrl: (ticket: Ticket): string => ticket.authorization_url,120121checkTicket: (ticket: Ticket): Promise<Ticket> =>122client.showTicket(ticket.id),123124exchangeTicket: (ticket: Ticket): Promise<AccessToken> =>125client.exchangeTicket(ticket.id),126127compareTokens: (a: AccessToken, b: AccessToken) =>128a.account_identifier === b.account_identifier,129};130131return authorizeAccessToken(provider);132}133134export function resolveTarget(135_account: AccountToken,136target: PublishRecord,137): Promise<PublishRecord | undefined> {138return Promise.resolve(target);139}140141function publish(142accountToken: AccountToken,143type: "document" | "site",144_input: string,145title: string,146slug: string,147render: (flags?: RenderFlags) => Promise<PublishFiles>,148_options: PublishOptions,149target?: PublishRecord,150): Promise<[PublishRecord, URL | undefined]> {151// Create an authorized QuartoPubClient.152const client = new QuartoPubClient(quartoPubEnvironment, accountToken.token);153154const handler: PublishHandler = {155name: kQuartoPub,156157slugAvailable: (slug: string) => client.slugAvailable(slug),158159createSite: (type: string, title: string, slug: string) =>160client.createSite(type, title, slug),161162createDeploy: (163siteId: string,164files: Record<string, string>,165size: number,166) => client.createDeploy(siteId, files, size),167168getDeploy: (deployId: string) => client.getDeploy(deployId),169170uploadDeployFile: (deployId: string, path: string, fileBody: Blob) =>171client.uploadDeployFile(deployId, path, fileBody),172173updateAccountSite: () => client.updateAccountSite(),174};175176return handlePublish(handler, type, title, slug, render, target);177}178179function isUnauthorized(err: Error) {180return err instanceof ApiError && err.status === 401;181}182183function isNotFound(err: Error) {184return err instanceof ApiError && err.status === 404;185}186187188