Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/publish/quarto-pub/quarto-pub.ts
6455 views
1
/*
2
* quarto-pub.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
7
import { AccessToken, Ticket } from "./api/types.ts";
8
import {
9
AccountToken,
10
AccountTokenType,
11
PublishFiles,
12
PublishProvider,
13
} from "../provider-types.ts";
14
import {
15
AuthorizationHandler,
16
authorizeAccessToken,
17
readAccessTokens,
18
writeAccessTokens,
19
} from "../common/account.ts";
20
import { handlePublish, PublishHandler } from "../common/publish.ts";
21
import { ApiError, PublishOptions, PublishRecord } from "../types.ts";
22
23
import { QuartoPubClient } from "./api/index.ts";
24
import { authorizePrompt } from "../account.ts";
25
import { quartoConfig } from "../../core/quarto.ts";
26
import { RenderFlags } from "../../command/render/types.ts";
27
28
export const kQuartoPub = "quarto-pub";
29
export const kQuartoPubAuthTokenVar = "QUARTO_PUB_AUTH_TOKEN";
30
31
export const quartoPubProvider: PublishProvider = {
32
name: kQuartoPub,
33
description: "Quarto Pub",
34
requiresServer: false,
35
listOriginOnly: false,
36
accountTokens,
37
authorizeToken,
38
removeToken,
39
resolveTarget,
40
publish,
41
isUnauthorized,
42
isNotFound,
43
};
44
45
function accountTokens() {
46
const envTk = environmentAuthToken();
47
const accessTks = readAccessTokens<AccessToken>(quartoPubProvider.name);
48
49
const accounts: AccountToken[] = [];
50
if (envTk) {
51
accounts.push({
52
type: AccountTokenType.Environment,
53
name: kQuartoPubAuthTokenVar,
54
server: null,
55
token: envTk,
56
});
57
}
58
59
if (accessTks) {
60
for (const accessTk of accessTks) {
61
accounts.push({
62
type: AccountTokenType.Authorized,
63
name: accessTk.email!,
64
server: null,
65
token: accessTk.application_token,
66
});
67
}
68
}
69
70
return Promise.resolve(accounts);
71
}
72
73
async function authorizeToken(_options: PublishOptions) {
74
if (await authorizePrompt(quartoPubProvider)) {
75
const token = await authorizeQuartoPubAccessToken();
76
if (token) {
77
return {
78
type: AccountTokenType.Authorized,
79
name: token.email!,
80
server: null,
81
token: token.application_token,
82
};
83
} else {
84
return undefined;
85
}
86
}
87
}
88
89
function removeToken(token: AccountToken) {
90
writeAccessTokens(
91
quartoPubProvider.name,
92
readAccessTokens<AccessToken>(quartoPubProvider.name)?.filter(
93
(accessToken) => {
94
return accessToken.email !== token.name;
95
},
96
) || [],
97
);
98
}
99
100
// Load the .env configuration and the environment.
101
const dotenvConfig = await quartoConfig.dotenv();
102
const quartoPubEnvironment = dotenvConfig["QUARTO_PUB_ENVIRONMENT"];
103
104
function environmentAuthToken() {
105
return Deno.env.get(kQuartoPubAuthTokenVar);
106
}
107
108
function authorizeQuartoPubAccessToken(): Promise<
109
AccessToken | undefined
110
> {
111
// Create an unauthorized QuartoPubClient.
112
const client = new QuartoPubClient(quartoPubEnvironment);
113
114
const provider: AuthorizationHandler<AccessToken, Ticket> = {
115
name: kQuartoPub,
116
117
createTicket: (): Promise<Ticket> =>
118
client.createTicket(dotenvConfig["QUARTO_PUB_APP_CLIENT_ID"]),
119
120
authorizationUrl: (ticket: Ticket): string => ticket.authorization_url,
121
122
checkTicket: (ticket: Ticket): Promise<Ticket> =>
123
client.showTicket(ticket.id),
124
125
exchangeTicket: (ticket: Ticket): Promise<AccessToken> =>
126
client.exchangeTicket(ticket.id),
127
128
compareTokens: (a: AccessToken, b: AccessToken) =>
129
a.account_identifier === b.account_identifier,
130
};
131
132
return authorizeAccessToken(provider);
133
}
134
135
export function resolveTarget(
136
_account: AccountToken,
137
target: PublishRecord,
138
): Promise<PublishRecord | undefined> {
139
return Promise.resolve(target);
140
}
141
142
function publish(
143
accountToken: AccountToken,
144
type: "document" | "site",
145
_input: string,
146
title: string,
147
slug: string,
148
render: (flags?: RenderFlags) => Promise<PublishFiles>,
149
_options: PublishOptions,
150
target?: PublishRecord,
151
): Promise<[PublishRecord, URL | undefined]> {
152
// Create an authorized QuartoPubClient.
153
const client = new QuartoPubClient(quartoPubEnvironment, accountToken.token);
154
155
const handler: PublishHandler = {
156
name: kQuartoPub,
157
158
slugAvailable: (slug: string) => client.slugAvailable(slug),
159
160
createSite: (type: string, title: string, slug: string) =>
161
client.createSite(type, title, slug),
162
163
createDeploy: (
164
siteId: string,
165
files: Record<string, string>,
166
size: number,
167
) => client.createDeploy(siteId, files, size),
168
169
getDeploy: (deployId: string) => client.getDeploy(deployId),
170
171
uploadDeployFile: (deployId: string, path: string, fileBody: Blob) =>
172
client.uploadDeployFile(deployId, path, fileBody),
173
174
updateAccountSite: () => client.updateAccountSite(),
175
};
176
177
return handlePublish(handler, type, title, slug, render, target);
178
}
179
180
function isUnauthorized(err: Error) {
181
return err instanceof ApiError && err.status === 401;
182
}
183
184
function isNotFound(err: Error) {
185
return err instanceof ApiError && err.status === 404;
186
}
187
188