@camstack/addon-google-services 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/google-services.addon.js +154 -63
- package/dist/google-services.addon.mjs +151 -64
- package/dist/index.js +4 -0
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
|
@@ -52805,8 +52805,6 @@ var GoogleServicesConfigSchema = object({
|
|
|
52805
52805
|
clientSecret: string().default("").meta({ secret: true }),
|
|
52806
52806
|
/** Comma-separated Google calendar ids to expose; empty = all. */
|
|
52807
52807
|
calendarFilter: string().default(""),
|
|
52808
|
-
/** Offer Gmail send via `smtp-provider` (same OAuth account). */
|
|
52809
|
-
gmailSendEnabled: boolean().default(false),
|
|
52810
52808
|
/** Default From for Gmail send — usually the connected account email. */
|
|
52811
52809
|
fromAddress: string().default(""),
|
|
52812
52810
|
fromName: string().default("")
|
|
@@ -52827,6 +52825,8 @@ var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
|
52827
52825
|
var GOOGLE_CALENDAR_API = "https://www.googleapis.com/calendar/v3";
|
|
52828
52826
|
var GOOGLE_GMAIL_API = "https://gmail.googleapis.com/gmail/v1";
|
|
52829
52827
|
var GOOGLE_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
|
|
52828
|
+
var OAUTH_START_PATH = `/addon/google-services/oauth/start`;
|
|
52829
|
+
var OAUTH_CALLBACK_SUFFIX = `/addon/google-services/oauth/callback`;
|
|
52830
52830
|
//#endregion
|
|
52831
52831
|
//#region src/google-calendar-source.ts
|
|
52832
52832
|
function parseGoogleInstant(value) {
|
|
@@ -52984,12 +52984,6 @@ function createGmailSmtpProvider(deps) {
|
|
|
52984
52984
|
const status = () => {
|
|
52985
52985
|
const cfg = deps.getConfig();
|
|
52986
52986
|
const email = deps.getAccountEmail() || cfg.fromAddress.trim() || "gmail";
|
|
52987
|
-
if (!cfg.gmailSendEnabled) return {
|
|
52988
|
-
ready: false,
|
|
52989
|
-
host: email,
|
|
52990
|
-
error: "Gmail send is disabled in Google Services settings.",
|
|
52991
|
-
lastVerifiedAt
|
|
52992
|
-
};
|
|
52993
52987
|
if (!deps.connected()) return {
|
|
52994
52988
|
ready: false,
|
|
52995
52989
|
host: email,
|
|
@@ -53006,10 +53000,6 @@ function createGmailSmtpProvider(deps) {
|
|
|
53006
53000
|
return {
|
|
53007
53001
|
getStatus: async () => status(),
|
|
53008
53002
|
verify: async () => {
|
|
53009
|
-
if (!deps.getConfig().gmailSendEnabled) {
|
|
53010
|
-
lastError = "Gmail send is disabled in Google Services settings.";
|
|
53011
|
-
return status();
|
|
53012
|
-
}
|
|
53013
53003
|
if (!deps.connected()) {
|
|
53014
53004
|
lastError = "Google account is not connected.";
|
|
53015
53005
|
return status();
|
|
@@ -53032,7 +53022,6 @@ function createGmailSmtpProvider(deps) {
|
|
|
53032
53022
|
},
|
|
53033
53023
|
sendEmail: async (input) => {
|
|
53034
53024
|
const cfg = deps.getConfig();
|
|
53035
|
-
if (!cfg.gmailSendEnabled) throw new Error("Gmail send is disabled in Google Services settings.");
|
|
53036
53025
|
if (!deps.connected()) throw new Error("Google account is not connected — open Connect Google.");
|
|
53037
53026
|
const from = input.from?.trim() || buildFromHeader(cfg, deps.getAccountEmail());
|
|
53038
53027
|
if (!from) throw new Error("Set From address in Google Services settings (or connect an account).");
|
|
@@ -53138,6 +53127,67 @@ function needsRefresh(state, nowMs = Date.now()) {
|
|
|
53138
53127
|
return state.accessExpiresAt <= nowMs;
|
|
53139
53128
|
}
|
|
53140
53129
|
//#endregion
|
|
53130
|
+
//#region src/google-settings-copy.ts
|
|
53131
|
+
/**
|
|
53132
|
+
* Operator-facing setup copy for Connectivity → Calendars / Email.
|
|
53133
|
+
* Keep Connect as a same-origin relative link so the admin session cookie
|
|
53134
|
+
* rides along (an absolute Public URL from a LAN admin tab drops the cookie).
|
|
53135
|
+
*/
|
|
53136
|
+
function redirectUriDisplay(publicOrigin) {
|
|
53137
|
+
if (publicOrigin === null || publicOrigin.length === 0) return `<public-hub>${OAUTH_CALLBACK_SUFFIX}`;
|
|
53138
|
+
return `${publicOrigin.replace(/\/$/, "")}${OAUTH_CALLBACK_SUFFIX}`;
|
|
53139
|
+
}
|
|
53140
|
+
function introHtml(cap, publicOrigin) {
|
|
53141
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
53142
|
+
if (cap === "smtp-provider") return [
|
|
53143
|
+
"<ol>",
|
|
53144
|
+
"<li>In Google Cloud Console enable the <b>Gmail API</b> (and Calendar API if you also use Calendars).</li>",
|
|
53145
|
+
"<li>Create an OAuth client of type <b>Web application</b>.</li>",
|
|
53146
|
+
`<li>Add this authorized redirect URI (Network → <b>Public endpoint</b>, not Internal): <code>${redirect}</code></li>`,
|
|
53147
|
+
"<li>Paste Client ID and Client secret below, then <b>Save</b>.</li>",
|
|
53148
|
+
`<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
|
|
53149
|
+
"<li>Set From address (connected account or a verified send-as). Turn this integration off with the page <b>Enable</b> toggle when you do not want Gmail send — Calendar can stay connected.</li>",
|
|
53150
|
+
"</ol>"
|
|
53151
|
+
].join("");
|
|
53152
|
+
if (cap === "calendar-source") return [
|
|
53153
|
+
"<ol>",
|
|
53154
|
+
"<li>In Google Cloud Console enable the <b>Google Calendar API</b> (and Gmail API if you also use Email).</li>",
|
|
53155
|
+
"<li>Create an OAuth client of type <b>Web application</b>.</li>",
|
|
53156
|
+
`<li>Add this authorized redirect URI (Network → <b>Public endpoint</b>, not Internal): <code>${redirect}</code></li>`,
|
|
53157
|
+
"<li>Paste Client ID and Client secret below, then <b>Save</b>.</li>",
|
|
53158
|
+
`<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
|
|
53159
|
+
"<li>Optionally restrict which calendars are exposed with the filter below.</li>",
|
|
53160
|
+
"</ol>"
|
|
53161
|
+
].join("");
|
|
53162
|
+
return [
|
|
53163
|
+
"<ol>",
|
|
53164
|
+
"<li>Enable <b>Google Calendar API</b> and <b>Gmail API</b> in Google Cloud Console.</li>",
|
|
53165
|
+
"<li>Create a <b>Web application</b> OAuth client.</li>",
|
|
53166
|
+
`<li>Register redirect URI: <code>${redirect}</code> (Network → Public endpoint).</li>`,
|
|
53167
|
+
"<li>Paste Client ID / secret, Save, then <a href=\"" + OAUTH_START_PATH + "\">Connect Google</a>.</li>",
|
|
53168
|
+
"<li>Calendars and Email share one Google account; use each page’s <b>Enable</b> toggle to turn a capability off without revoking OAuth.</li>",
|
|
53169
|
+
"</ol>"
|
|
53170
|
+
].join("");
|
|
53171
|
+
}
|
|
53172
|
+
function connectHtml(cap, publicOrigin) {
|
|
53173
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
53174
|
+
if (cap === "smtp-provider") return [
|
|
53175
|
+
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
53176
|
+
"Connect requests <code>gmail.send</code> (and Calendar read for the Calendars page). ",
|
|
53177
|
+
`Google must list exactly: <code>${redirect}</code>. `,
|
|
53178
|
+
"If an older Connect predates Gmail send, revoke CamStack in Google Account → Security and Connect again."
|
|
53179
|
+
].join("");
|
|
53180
|
+
if (cap === "calendar-source") return [
|
|
53181
|
+
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
53182
|
+
"Connect requests <code>calendar.readonly</code> (and Gmail send for the Email page). ",
|
|
53183
|
+
`Google must list exactly: <code>${redirect}</code>.`
|
|
53184
|
+
].join("");
|
|
53185
|
+
return [`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a>. `, `Redirect URI registered in Google Cloud must be <code>${redirect}</code>.`].join("");
|
|
53186
|
+
}
|
|
53187
|
+
function escapeHtml$1(s) {
|
|
53188
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
53189
|
+
}
|
|
53190
|
+
//#endregion
|
|
53141
53191
|
//#region src/oauth-redirect-origin.ts
|
|
53142
53192
|
/**
|
|
53143
53193
|
* Google OAuth redirect origin — same role as hub OAuth2 / sign-in
|
|
@@ -53167,12 +53217,15 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
53167
53217
|
oauth = { ...DEFAULT_OAUTH_STATE };
|
|
53168
53218
|
/** CSRF state → issuedAt ms */
|
|
53169
53219
|
pendingStates = /* @__PURE__ */ new Map();
|
|
53220
|
+
/** Cached Public-endpoint origin for settings copy (redirect URI display). */
|
|
53221
|
+
publicOrigin = null;
|
|
53170
53222
|
constructor() {
|
|
53171
53223
|
super({ ...DEFAULT_GOOGLE_CONFIG });
|
|
53172
53224
|
}
|
|
53173
53225
|
async onInitialize() {
|
|
53174
53226
|
this.oauthHandle = this.state("oauth", OauthStateSchema, { ...DEFAULT_OAUTH_STATE });
|
|
53175
53227
|
this.oauth = await this.oauthHandle.get();
|
|
53228
|
+
await this.refreshPublicOrigin();
|
|
53176
53229
|
const api = createGoogleCalendarApi(async () => this.ensureAccessToken());
|
|
53177
53230
|
const calendarProvider = createGoogleCalendarSource({
|
|
53178
53231
|
logger: this.ctx.logger,
|
|
@@ -53203,7 +53256,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
53203
53256
|
this.ctx.logger.info("Initialized — google calendar-source + gmail smtp", { meta: {
|
|
53204
53257
|
connected: this.oauth.refreshToken.length > 0,
|
|
53205
53258
|
accountEmail: this.oauth.accountEmail || void 0,
|
|
53206
|
-
|
|
53259
|
+
publicOrigin: this.publicOrigin ?? void 0
|
|
53207
53260
|
} });
|
|
53208
53261
|
return { providers: [
|
|
53209
53262
|
{
|
|
@@ -53320,69 +53373,103 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
53320
53373
|
return new Set(raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0));
|
|
53321
53374
|
}
|
|
53322
53375
|
async resolveRedirectUri() {
|
|
53323
|
-
|
|
53324
|
-
if (!
|
|
53325
|
-
return `${
|
|
53376
|
+
await this.refreshPublicOrigin();
|
|
53377
|
+
if (!this.publicOrigin) throw new Error("No hub public address available from local-network — set a Public endpoint on the Network page (OAuth2 redirects use that role, not Internal).");
|
|
53378
|
+
return `${this.publicOrigin.replace(/\/$/, "")}/addon/${ADDON_ID}${CALLBACK_PATH}`;
|
|
53326
53379
|
}
|
|
53327
|
-
|
|
53328
|
-
|
|
53329
|
-
|
|
53330
|
-
|
|
53331
|
-
|
|
53332
|
-
|
|
53333
|
-
|
|
53334
|
-
|
|
53335
|
-
|
|
53336
|
-
|
|
53337
|
-
|
|
53338
|
-
|
|
53339
|
-
|
|
53340
|
-
|
|
53341
|
-
|
|
53342
|
-
|
|
53343
|
-
|
|
53344
|
-
|
|
53345
|
-
|
|
53380
|
+
async refreshPublicOrigin() {
|
|
53381
|
+
try {
|
|
53382
|
+
const book = await this.ctx.api.localNetwork.getAddresses.query({});
|
|
53383
|
+
this.publicOrigin = pickOauthRedirectOrigin(book);
|
|
53384
|
+
} catch {
|
|
53385
|
+
this.publicOrigin = null;
|
|
53386
|
+
}
|
|
53387
|
+
}
|
|
53388
|
+
/**
|
|
53389
|
+
* Connectivity asks for settings scoped to the page capability, so Calendar
|
|
53390
|
+
* and Email do not each redraw the other's fields (same pattern as
|
|
53391
|
+
* mqtt-broker under `broker`). The addon page (no cap) keeps every section.
|
|
53392
|
+
*/
|
|
53393
|
+
globalSettingsSchema(cap) {
|
|
53394
|
+
const scoped = asSettingsCap(cap);
|
|
53395
|
+
return this.schema({ sections: [
|
|
53396
|
+
{
|
|
53397
|
+
id: "setup-intro",
|
|
53398
|
+
title: "Setup",
|
|
53399
|
+
description: scoped === "smtp-provider" ? "Connect a Google account for Gmail send (same OAuth as Calendars)." : scoped === "calendar-source" ? "Connect a Google account for Calendar (same OAuth as Email)." : "One Google account for Calendar and optional Gmail send.",
|
|
53400
|
+
columns: 1,
|
|
53401
|
+
fields: [{
|
|
53402
|
+
type: "info",
|
|
53403
|
+
key: "_setupSteps",
|
|
53404
|
+
label: "Steps",
|
|
53405
|
+
format: "html",
|
|
53406
|
+
content: introHtml(scoped, this.publicOrigin)
|
|
53407
|
+
}]
|
|
53408
|
+
},
|
|
53409
|
+
{
|
|
53410
|
+
id: "google-oauth",
|
|
53411
|
+
title: "Google account",
|
|
53412
|
+
description: "OAuth client credentials (Web application) stored in the hub keychain.",
|
|
53413
|
+
columns: 1,
|
|
53414
|
+
fields: [
|
|
53415
|
+
{
|
|
53416
|
+
type: "text",
|
|
53417
|
+
key: "clientId",
|
|
53418
|
+
label: "Client ID"
|
|
53419
|
+
},
|
|
53420
|
+
{
|
|
53421
|
+
type: "password",
|
|
53422
|
+
key: "clientSecret",
|
|
53423
|
+
label: "Client secret",
|
|
53424
|
+
showToggle: true
|
|
53425
|
+
},
|
|
53426
|
+
{
|
|
53427
|
+
type: "info",
|
|
53428
|
+
key: "_connectInfo",
|
|
53429
|
+
label: "Connect",
|
|
53430
|
+
format: "html",
|
|
53431
|
+
content: connectHtml(scoped, this.publicOrigin)
|
|
53432
|
+
}
|
|
53433
|
+
]
|
|
53434
|
+
},
|
|
53435
|
+
...scoped === "smtp-provider" ? [] : [{
|
|
53436
|
+
id: "google-calendar",
|
|
53437
|
+
title: "Google Calendar",
|
|
53438
|
+
description: "Which calendars this hub exposes as calendar-source entries.",
|
|
53439
|
+
columns: 1,
|
|
53440
|
+
fields: [{
|
|
53346
53441
|
type: "text",
|
|
53347
53442
|
key: "calendarFilter",
|
|
53348
53443
|
label: "Calendar id filter (optional)",
|
|
53349
53444
|
description: "Comma-separated Google calendar ids to expose. Empty = all calendars the account can read."
|
|
53350
|
-
}
|
|
53351
|
-
|
|
53352
|
-
|
|
53353
|
-
|
|
53354
|
-
|
|
53355
|
-
|
|
53356
|
-
|
|
53357
|
-
|
|
53358
|
-
]
|
|
53359
|
-
}, {
|
|
53360
|
-
id: "gmail-send",
|
|
53361
|
-
title: "Gmail send",
|
|
53362
|
-
description: "Optional email delivery via the Gmail API (`smtp-provider`). Uses the same OAuth account — From must be that account or a verified send-as alias.",
|
|
53363
|
-
columns: 1,
|
|
53364
|
-
fields: [
|
|
53365
|
-
{
|
|
53366
|
-
type: "boolean",
|
|
53367
|
-
key: "gmailSendEnabled",
|
|
53368
|
-
label: "Enable Gmail send",
|
|
53369
|
-
description: "When off, Calendar still works; this addon reports smtp-provider not ready."
|
|
53370
|
-
},
|
|
53371
|
-
{
|
|
53445
|
+
}]
|
|
53446
|
+
}],
|
|
53447
|
+
...scoped === "calendar-source" ? [] : [{
|
|
53448
|
+
id: "gmail-send",
|
|
53449
|
+
title: "Gmail send",
|
|
53450
|
+
description: "Delivery via the Gmail API. Disable this capability with the page Enable toggle — not a second switch here. From must be the connected account or a verified send-as.",
|
|
53451
|
+
columns: 1,
|
|
53452
|
+
fields: [{
|
|
53372
53453
|
type: "text",
|
|
53373
53454
|
key: "fromAddress",
|
|
53374
53455
|
label: "From address",
|
|
53375
53456
|
description: "Defaults to the connected Google account email when empty."
|
|
53376
|
-
},
|
|
53377
|
-
{
|
|
53457
|
+
}, {
|
|
53378
53458
|
type: "text",
|
|
53379
53459
|
key: "fromName",
|
|
53380
53460
|
label: "From display name (optional)"
|
|
53381
|
-
}
|
|
53382
|
-
]
|
|
53383
|
-
|
|
53461
|
+
}]
|
|
53462
|
+
}]
|
|
53463
|
+
] });
|
|
53464
|
+
}
|
|
53465
|
+
async getGlobalSettings(overlay, cap, nodeId) {
|
|
53466
|
+
await this.refreshPublicOrigin();
|
|
53467
|
+
return super.getGlobalSettings(overlay, cap, nodeId);
|
|
53384
53468
|
}
|
|
53385
53469
|
};
|
|
53470
|
+
function asSettingsCap(cap) {
|
|
53471
|
+
if (cap === "calendar-source" || cap === "smtp-provider") return cap;
|
|
53472
|
+
}
|
|
53386
53473
|
function escapeHtml(s) {
|
|
53387
53474
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
53388
53475
|
}
|
|
@@ -53393,13 +53480,17 @@ exports.GOOGLE_OAUTH_SCOPES = GOOGLE_OAUTH_SCOPES;
|
|
|
53393
53480
|
exports.GoogleServicesAddon = GoogleServicesAddon;
|
|
53394
53481
|
exports.default = GoogleServicesAddon;
|
|
53395
53482
|
exports.GoogleServicesConfigSchema = GoogleServicesConfigSchema;
|
|
53483
|
+
exports.OAUTH_START_PATH = OAUTH_START_PATH;
|
|
53396
53484
|
exports.applyTokenResponse = applyTokenResponse;
|
|
53397
53485
|
exports.buildAuthorizeUrl = buildAuthorizeUrl;
|
|
53398
53486
|
exports.buildRawMime = buildRawMime;
|
|
53487
|
+
exports.connectHtml = connectHtml;
|
|
53399
53488
|
exports.createGmailSmtpProvider = createGmailSmtpProvider;
|
|
53400
53489
|
exports.createGoogleCalendarApi = createGoogleCalendarApi;
|
|
53401
53490
|
exports.createGoogleCalendarSource = createGoogleCalendarSource;
|
|
53402
53491
|
exports.exchangeCode = exchangeCode;
|
|
53492
|
+
exports.introHtml = introHtml;
|
|
53403
53493
|
exports.mapGoogleEvent = mapGoogleEvent;
|
|
53404
53494
|
exports.needsRefresh = needsRefresh;
|
|
53495
|
+
exports.redirectUriDisplay = redirectUriDisplay;
|
|
53405
53496
|
exports.refreshAccessToken = refreshAccessToken;
|
|
@@ -52801,8 +52801,6 @@ var GoogleServicesConfigSchema = object({
|
|
|
52801
52801
|
clientSecret: string().default("").meta({ secret: true }),
|
|
52802
52802
|
/** Comma-separated Google calendar ids to expose; empty = all. */
|
|
52803
52803
|
calendarFilter: string().default(""),
|
|
52804
|
-
/** Offer Gmail send via `smtp-provider` (same OAuth account). */
|
|
52805
|
-
gmailSendEnabled: boolean().default(false),
|
|
52806
52804
|
/** Default From for Gmail send — usually the connected account email. */
|
|
52807
52805
|
fromAddress: string().default(""),
|
|
52808
52806
|
fromName: string().default("")
|
|
@@ -52823,6 +52821,8 @@ var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
|
52823
52821
|
var GOOGLE_CALENDAR_API = "https://www.googleapis.com/calendar/v3";
|
|
52824
52822
|
var GOOGLE_GMAIL_API = "https://gmail.googleapis.com/gmail/v1";
|
|
52825
52823
|
var GOOGLE_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
|
|
52824
|
+
var OAUTH_START_PATH = `/addon/google-services/oauth/start`;
|
|
52825
|
+
var OAUTH_CALLBACK_SUFFIX = `/addon/google-services/oauth/callback`;
|
|
52826
52826
|
//#endregion
|
|
52827
52827
|
//#region src/google-calendar-source.ts
|
|
52828
52828
|
function parseGoogleInstant(value) {
|
|
@@ -52980,12 +52980,6 @@ function createGmailSmtpProvider(deps) {
|
|
|
52980
52980
|
const status = () => {
|
|
52981
52981
|
const cfg = deps.getConfig();
|
|
52982
52982
|
const email = deps.getAccountEmail() || cfg.fromAddress.trim() || "gmail";
|
|
52983
|
-
if (!cfg.gmailSendEnabled) return {
|
|
52984
|
-
ready: false,
|
|
52985
|
-
host: email,
|
|
52986
|
-
error: "Gmail send is disabled in Google Services settings.",
|
|
52987
|
-
lastVerifiedAt
|
|
52988
|
-
};
|
|
52989
52983
|
if (!deps.connected()) return {
|
|
52990
52984
|
ready: false,
|
|
52991
52985
|
host: email,
|
|
@@ -53002,10 +52996,6 @@ function createGmailSmtpProvider(deps) {
|
|
|
53002
52996
|
return {
|
|
53003
52997
|
getStatus: async () => status(),
|
|
53004
52998
|
verify: async () => {
|
|
53005
|
-
if (!deps.getConfig().gmailSendEnabled) {
|
|
53006
|
-
lastError = "Gmail send is disabled in Google Services settings.";
|
|
53007
|
-
return status();
|
|
53008
|
-
}
|
|
53009
52999
|
if (!deps.connected()) {
|
|
53010
53000
|
lastError = "Google account is not connected.";
|
|
53011
53001
|
return status();
|
|
@@ -53028,7 +53018,6 @@ function createGmailSmtpProvider(deps) {
|
|
|
53028
53018
|
},
|
|
53029
53019
|
sendEmail: async (input) => {
|
|
53030
53020
|
const cfg = deps.getConfig();
|
|
53031
|
-
if (!cfg.gmailSendEnabled) throw new Error("Gmail send is disabled in Google Services settings.");
|
|
53032
53021
|
if (!deps.connected()) throw new Error("Google account is not connected — open Connect Google.");
|
|
53033
53022
|
const from = input.from?.trim() || buildFromHeader(cfg, deps.getAccountEmail());
|
|
53034
53023
|
if (!from) throw new Error("Set From address in Google Services settings (or connect an account).");
|
|
@@ -53134,6 +53123,67 @@ function needsRefresh(state, nowMs = Date.now()) {
|
|
|
53134
53123
|
return state.accessExpiresAt <= nowMs;
|
|
53135
53124
|
}
|
|
53136
53125
|
//#endregion
|
|
53126
|
+
//#region src/google-settings-copy.ts
|
|
53127
|
+
/**
|
|
53128
|
+
* Operator-facing setup copy for Connectivity → Calendars / Email.
|
|
53129
|
+
* Keep Connect as a same-origin relative link so the admin session cookie
|
|
53130
|
+
* rides along (an absolute Public URL from a LAN admin tab drops the cookie).
|
|
53131
|
+
*/
|
|
53132
|
+
function redirectUriDisplay(publicOrigin) {
|
|
53133
|
+
if (publicOrigin === null || publicOrigin.length === 0) return `<public-hub>${OAUTH_CALLBACK_SUFFIX}`;
|
|
53134
|
+
return `${publicOrigin.replace(/\/$/, "")}${OAUTH_CALLBACK_SUFFIX}`;
|
|
53135
|
+
}
|
|
53136
|
+
function introHtml(cap, publicOrigin) {
|
|
53137
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
53138
|
+
if (cap === "smtp-provider") return [
|
|
53139
|
+
"<ol>",
|
|
53140
|
+
"<li>In Google Cloud Console enable the <b>Gmail API</b> (and Calendar API if you also use Calendars).</li>",
|
|
53141
|
+
"<li>Create an OAuth client of type <b>Web application</b>.</li>",
|
|
53142
|
+
`<li>Add this authorized redirect URI (Network → <b>Public endpoint</b>, not Internal): <code>${redirect}</code></li>`,
|
|
53143
|
+
"<li>Paste Client ID and Client secret below, then <b>Save</b>.</li>",
|
|
53144
|
+
`<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
|
|
53145
|
+
"<li>Set From address (connected account or a verified send-as). Turn this integration off with the page <b>Enable</b> toggle when you do not want Gmail send — Calendar can stay connected.</li>",
|
|
53146
|
+
"</ol>"
|
|
53147
|
+
].join("");
|
|
53148
|
+
if (cap === "calendar-source") return [
|
|
53149
|
+
"<ol>",
|
|
53150
|
+
"<li>In Google Cloud Console enable the <b>Google Calendar API</b> (and Gmail API if you also use Email).</li>",
|
|
53151
|
+
"<li>Create an OAuth client of type <b>Web application</b>.</li>",
|
|
53152
|
+
`<li>Add this authorized redirect URI (Network → <b>Public endpoint</b>, not Internal): <code>${redirect}</code></li>`,
|
|
53153
|
+
"<li>Paste Client ID and Client secret below, then <b>Save</b>.</li>",
|
|
53154
|
+
`<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
|
|
53155
|
+
"<li>Optionally restrict which calendars are exposed with the filter below.</li>",
|
|
53156
|
+
"</ol>"
|
|
53157
|
+
].join("");
|
|
53158
|
+
return [
|
|
53159
|
+
"<ol>",
|
|
53160
|
+
"<li>Enable <b>Google Calendar API</b> and <b>Gmail API</b> in Google Cloud Console.</li>",
|
|
53161
|
+
"<li>Create a <b>Web application</b> OAuth client.</li>",
|
|
53162
|
+
`<li>Register redirect URI: <code>${redirect}</code> (Network → Public endpoint).</li>`,
|
|
53163
|
+
"<li>Paste Client ID / secret, Save, then <a href=\"" + OAUTH_START_PATH + "\">Connect Google</a>.</li>",
|
|
53164
|
+
"<li>Calendars and Email share one Google account; use each page’s <b>Enable</b> toggle to turn a capability off without revoking OAuth.</li>",
|
|
53165
|
+
"</ol>"
|
|
53166
|
+
].join("");
|
|
53167
|
+
}
|
|
53168
|
+
function connectHtml(cap, publicOrigin) {
|
|
53169
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
53170
|
+
if (cap === "smtp-provider") return [
|
|
53171
|
+
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
53172
|
+
"Connect requests <code>gmail.send</code> (and Calendar read for the Calendars page). ",
|
|
53173
|
+
`Google must list exactly: <code>${redirect}</code>. `,
|
|
53174
|
+
"If an older Connect predates Gmail send, revoke CamStack in Google Account → Security and Connect again."
|
|
53175
|
+
].join("");
|
|
53176
|
+
if (cap === "calendar-source") return [
|
|
53177
|
+
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
53178
|
+
"Connect requests <code>calendar.readonly</code> (and Gmail send for the Email page). ",
|
|
53179
|
+
`Google must list exactly: <code>${redirect}</code>.`
|
|
53180
|
+
].join("");
|
|
53181
|
+
return [`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a>. `, `Redirect URI registered in Google Cloud must be <code>${redirect}</code>.`].join("");
|
|
53182
|
+
}
|
|
53183
|
+
function escapeHtml$1(s) {
|
|
53184
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
53185
|
+
}
|
|
53186
|
+
//#endregion
|
|
53137
53187
|
//#region src/oauth-redirect-origin.ts
|
|
53138
53188
|
/**
|
|
53139
53189
|
* Google OAuth redirect origin — same role as hub OAuth2 / sign-in
|
|
@@ -53163,12 +53213,15 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
53163
53213
|
oauth = { ...DEFAULT_OAUTH_STATE };
|
|
53164
53214
|
/** CSRF state → issuedAt ms */
|
|
53165
53215
|
pendingStates = /* @__PURE__ */ new Map();
|
|
53216
|
+
/** Cached Public-endpoint origin for settings copy (redirect URI display). */
|
|
53217
|
+
publicOrigin = null;
|
|
53166
53218
|
constructor() {
|
|
53167
53219
|
super({ ...DEFAULT_GOOGLE_CONFIG });
|
|
53168
53220
|
}
|
|
53169
53221
|
async onInitialize() {
|
|
53170
53222
|
this.oauthHandle = this.state("oauth", OauthStateSchema, { ...DEFAULT_OAUTH_STATE });
|
|
53171
53223
|
this.oauth = await this.oauthHandle.get();
|
|
53224
|
+
await this.refreshPublicOrigin();
|
|
53172
53225
|
const api = createGoogleCalendarApi(async () => this.ensureAccessToken());
|
|
53173
53226
|
const calendarProvider = createGoogleCalendarSource({
|
|
53174
53227
|
logger: this.ctx.logger,
|
|
@@ -53199,7 +53252,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
53199
53252
|
this.ctx.logger.info("Initialized — google calendar-source + gmail smtp", { meta: {
|
|
53200
53253
|
connected: this.oauth.refreshToken.length > 0,
|
|
53201
53254
|
accountEmail: this.oauth.accountEmail || void 0,
|
|
53202
|
-
|
|
53255
|
+
publicOrigin: this.publicOrigin ?? void 0
|
|
53203
53256
|
} });
|
|
53204
53257
|
return { providers: [
|
|
53205
53258
|
{
|
|
@@ -53316,71 +53369,105 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
53316
53369
|
return new Set(raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0));
|
|
53317
53370
|
}
|
|
53318
53371
|
async resolveRedirectUri() {
|
|
53319
|
-
|
|
53320
|
-
if (!
|
|
53321
|
-
return `${
|
|
53372
|
+
await this.refreshPublicOrigin();
|
|
53373
|
+
if (!this.publicOrigin) throw new Error("No hub public address available from local-network — set a Public endpoint on the Network page (OAuth2 redirects use that role, not Internal).");
|
|
53374
|
+
return `${this.publicOrigin.replace(/\/$/, "")}/addon/${ADDON_ID}${CALLBACK_PATH}`;
|
|
53322
53375
|
}
|
|
53323
|
-
|
|
53324
|
-
|
|
53325
|
-
|
|
53326
|
-
|
|
53327
|
-
|
|
53328
|
-
|
|
53329
|
-
|
|
53330
|
-
|
|
53331
|
-
|
|
53332
|
-
|
|
53333
|
-
|
|
53334
|
-
|
|
53335
|
-
|
|
53336
|
-
|
|
53337
|
-
|
|
53338
|
-
|
|
53339
|
-
|
|
53340
|
-
|
|
53341
|
-
|
|
53376
|
+
async refreshPublicOrigin() {
|
|
53377
|
+
try {
|
|
53378
|
+
const book = await this.ctx.api.localNetwork.getAddresses.query({});
|
|
53379
|
+
this.publicOrigin = pickOauthRedirectOrigin(book);
|
|
53380
|
+
} catch {
|
|
53381
|
+
this.publicOrigin = null;
|
|
53382
|
+
}
|
|
53383
|
+
}
|
|
53384
|
+
/**
|
|
53385
|
+
* Connectivity asks for settings scoped to the page capability, so Calendar
|
|
53386
|
+
* and Email do not each redraw the other's fields (same pattern as
|
|
53387
|
+
* mqtt-broker under `broker`). The addon page (no cap) keeps every section.
|
|
53388
|
+
*/
|
|
53389
|
+
globalSettingsSchema(cap) {
|
|
53390
|
+
const scoped = asSettingsCap(cap);
|
|
53391
|
+
return this.schema({ sections: [
|
|
53392
|
+
{
|
|
53393
|
+
id: "setup-intro",
|
|
53394
|
+
title: "Setup",
|
|
53395
|
+
description: scoped === "smtp-provider" ? "Connect a Google account for Gmail send (same OAuth as Calendars)." : scoped === "calendar-source" ? "Connect a Google account for Calendar (same OAuth as Email)." : "One Google account for Calendar and optional Gmail send.",
|
|
53396
|
+
columns: 1,
|
|
53397
|
+
fields: [{
|
|
53398
|
+
type: "info",
|
|
53399
|
+
key: "_setupSteps",
|
|
53400
|
+
label: "Steps",
|
|
53401
|
+
format: "html",
|
|
53402
|
+
content: introHtml(scoped, this.publicOrigin)
|
|
53403
|
+
}]
|
|
53404
|
+
},
|
|
53405
|
+
{
|
|
53406
|
+
id: "google-oauth",
|
|
53407
|
+
title: "Google account",
|
|
53408
|
+
description: "OAuth client credentials (Web application) stored in the hub keychain.",
|
|
53409
|
+
columns: 1,
|
|
53410
|
+
fields: [
|
|
53411
|
+
{
|
|
53412
|
+
type: "text",
|
|
53413
|
+
key: "clientId",
|
|
53414
|
+
label: "Client ID"
|
|
53415
|
+
},
|
|
53416
|
+
{
|
|
53417
|
+
type: "password",
|
|
53418
|
+
key: "clientSecret",
|
|
53419
|
+
label: "Client secret",
|
|
53420
|
+
showToggle: true
|
|
53421
|
+
},
|
|
53422
|
+
{
|
|
53423
|
+
type: "info",
|
|
53424
|
+
key: "_connectInfo",
|
|
53425
|
+
label: "Connect",
|
|
53426
|
+
format: "html",
|
|
53427
|
+
content: connectHtml(scoped, this.publicOrigin)
|
|
53428
|
+
}
|
|
53429
|
+
]
|
|
53430
|
+
},
|
|
53431
|
+
...scoped === "smtp-provider" ? [] : [{
|
|
53432
|
+
id: "google-calendar",
|
|
53433
|
+
title: "Google Calendar",
|
|
53434
|
+
description: "Which calendars this hub exposes as calendar-source entries.",
|
|
53435
|
+
columns: 1,
|
|
53436
|
+
fields: [{
|
|
53342
53437
|
type: "text",
|
|
53343
53438
|
key: "calendarFilter",
|
|
53344
53439
|
label: "Calendar id filter (optional)",
|
|
53345
53440
|
description: "Comma-separated Google calendar ids to expose. Empty = all calendars the account can read."
|
|
53346
|
-
}
|
|
53347
|
-
|
|
53348
|
-
|
|
53349
|
-
|
|
53350
|
-
|
|
53351
|
-
|
|
53352
|
-
|
|
53353
|
-
|
|
53354
|
-
]
|
|
53355
|
-
}, {
|
|
53356
|
-
id: "gmail-send",
|
|
53357
|
-
title: "Gmail send",
|
|
53358
|
-
description: "Optional email delivery via the Gmail API (`smtp-provider`). Uses the same OAuth account — From must be that account or a verified send-as alias.",
|
|
53359
|
-
columns: 1,
|
|
53360
|
-
fields: [
|
|
53361
|
-
{
|
|
53362
|
-
type: "boolean",
|
|
53363
|
-
key: "gmailSendEnabled",
|
|
53364
|
-
label: "Enable Gmail send",
|
|
53365
|
-
description: "When off, Calendar still works; this addon reports smtp-provider not ready."
|
|
53366
|
-
},
|
|
53367
|
-
{
|
|
53441
|
+
}]
|
|
53442
|
+
}],
|
|
53443
|
+
...scoped === "calendar-source" ? [] : [{
|
|
53444
|
+
id: "gmail-send",
|
|
53445
|
+
title: "Gmail send",
|
|
53446
|
+
description: "Delivery via the Gmail API. Disable this capability with the page Enable toggle — not a second switch here. From must be the connected account or a verified send-as.",
|
|
53447
|
+
columns: 1,
|
|
53448
|
+
fields: [{
|
|
53368
53449
|
type: "text",
|
|
53369
53450
|
key: "fromAddress",
|
|
53370
53451
|
label: "From address",
|
|
53371
53452
|
description: "Defaults to the connected Google account email when empty."
|
|
53372
|
-
},
|
|
53373
|
-
{
|
|
53453
|
+
}, {
|
|
53374
53454
|
type: "text",
|
|
53375
53455
|
key: "fromName",
|
|
53376
53456
|
label: "From display name (optional)"
|
|
53377
|
-
}
|
|
53378
|
-
]
|
|
53379
|
-
|
|
53457
|
+
}]
|
|
53458
|
+
}]
|
|
53459
|
+
] });
|
|
53460
|
+
}
|
|
53461
|
+
async getGlobalSettings(overlay, cap, nodeId) {
|
|
53462
|
+
await this.refreshPublicOrigin();
|
|
53463
|
+
return super.getGlobalSettings(overlay, cap, nodeId);
|
|
53380
53464
|
}
|
|
53381
53465
|
};
|
|
53466
|
+
function asSettingsCap(cap) {
|
|
53467
|
+
if (cap === "calendar-source" || cap === "smtp-provider") return cap;
|
|
53468
|
+
}
|
|
53382
53469
|
function escapeHtml(s) {
|
|
53383
53470
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
53384
53471
|
}
|
|
53385
53472
|
//#endregion
|
|
53386
|
-
export { GoogleServicesAddon, GoogleServicesAddon as default,
|
|
53473
|
+
export { GoogleServicesAddon, GoogleServicesAddon as default, GoogleServicesConfigSchema as _, buildAuthorizeUrl as a, refreshAccessToken as c, createGoogleCalendarApi as d, createGoogleCalendarSource as f, GOOGLE_OAUTH_SCOPES as g, GOOGLE_GMAIL_SEND_SCOPE as h, applyTokenResponse as i, buildRawMime as l, DEFAULT_GOOGLE_CONFIG as m, introHtml as n, exchangeCode as o, mapGoogleEvent as p, redirectUriDisplay as r, needsRefresh as s, connectHtml as t, createGmailSmtpProvider as u, OAUTH_START_PATH as v };
|
package/dist/index.js
CHANGED
|
@@ -8,14 +8,18 @@ exports.GOOGLE_GMAIL_SEND_SCOPE = require_google_services_addon.GOOGLE_GMAIL_SEN
|
|
|
8
8
|
exports.GOOGLE_OAUTH_SCOPES = require_google_services_addon.GOOGLE_OAUTH_SCOPES;
|
|
9
9
|
exports.GoogleServicesAddon = require_google_services_addon.GoogleServicesAddon;
|
|
10
10
|
exports.GoogleServicesConfigSchema = require_google_services_addon.GoogleServicesConfigSchema;
|
|
11
|
+
exports.OAUTH_START_PATH = require_google_services_addon.OAUTH_START_PATH;
|
|
11
12
|
exports.applyTokenResponse = require_google_services_addon.applyTokenResponse;
|
|
12
13
|
exports.buildAuthorizeUrl = require_google_services_addon.buildAuthorizeUrl;
|
|
13
14
|
exports.buildRawMime = require_google_services_addon.buildRawMime;
|
|
15
|
+
exports.connectHtml = require_google_services_addon.connectHtml;
|
|
14
16
|
exports.createGmailSmtpProvider = require_google_services_addon.createGmailSmtpProvider;
|
|
15
17
|
exports.createGoogleCalendarApi = require_google_services_addon.createGoogleCalendarApi;
|
|
16
18
|
exports.createGoogleCalendarSource = require_google_services_addon.createGoogleCalendarSource;
|
|
17
19
|
exports.default = require_google_services_addon.GoogleServicesAddon;
|
|
18
20
|
exports.exchangeCode = require_google_services_addon.exchangeCode;
|
|
21
|
+
exports.introHtml = require_google_services_addon.introHtml;
|
|
19
22
|
exports.mapGoogleEvent = require_google_services_addon.mapGoogleEvent;
|
|
20
23
|
exports.needsRefresh = require_google_services_addon.needsRefresh;
|
|
24
|
+
exports.redirectUriDisplay = require_google_services_addon.redirectUriDisplay;
|
|
21
25
|
exports.refreshAccessToken = require_google_services_addon.refreshAccessToken;
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { GoogleServicesAddon, a as
|
|
2
|
-
export { DEFAULT_GOOGLE_CONFIG, GOOGLE_GMAIL_SEND_SCOPE, GOOGLE_OAUTH_SCOPES, GoogleServicesAddon, GoogleServicesAddon as default, GoogleServicesConfigSchema, applyTokenResponse, buildAuthorizeUrl, buildRawMime, createGmailSmtpProvider, createGoogleCalendarApi, createGoogleCalendarSource, exchangeCode, mapGoogleEvent, needsRefresh, refreshAccessToken };
|
|
1
|
+
import { GoogleServicesAddon, _ as GoogleServicesConfigSchema, a as buildAuthorizeUrl, c as refreshAccessToken, d as createGoogleCalendarApi, f as createGoogleCalendarSource, g as GOOGLE_OAUTH_SCOPES, h as GOOGLE_GMAIL_SEND_SCOPE, i as applyTokenResponse, l as buildRawMime, m as DEFAULT_GOOGLE_CONFIG, n as introHtml, o as exchangeCode, p as mapGoogleEvent, r as redirectUriDisplay, s as needsRefresh, t as connectHtml, u as createGmailSmtpProvider, v as OAUTH_START_PATH } from "./google-services.addon.mjs";
|
|
2
|
+
export { DEFAULT_GOOGLE_CONFIG, GOOGLE_GMAIL_SEND_SCOPE, GOOGLE_OAUTH_SCOPES, GoogleServicesAddon, GoogleServicesAddon as default, GoogleServicesConfigSchema, OAUTH_START_PATH, applyTokenResponse, buildAuthorizeUrl, buildRawMime, connectHtml, createGmailSmtpProvider, createGoogleCalendarApi, createGoogleCalendarSource, exchangeCode, introHtml, mapGoogleEvent, needsRefresh, redirectUriDisplay, refreshAccessToken };
|
package/package.json
CHANGED