v2.2.17: Google Service Control & Astra Office Flow Layer
This commit is contained in:
@@ -37,6 +37,8 @@ const TELEGRAM_TOKEN_SECRET_KEY = 'g1nation.telegram.botToken';
|
||||
|
||||
export interface SettingsPanelDeps {
|
||||
extensionUri: vscode.Uri;
|
||||
/** Used by features that store data in workspaceState / globalState (Google OAuth tokens etc). */
|
||||
context: vscode.ExtensionContext;
|
||||
secrets: vscode.SecretStorage;
|
||||
/** Returns the live Telegram client so we can call getMe for "test connection". */
|
||||
telegramClient: ITelegramClient;
|
||||
@@ -81,6 +83,21 @@ interface SettingsState {
|
||||
maxAutoSteps: number;
|
||||
maxContextSize: number;
|
||||
};
|
||||
google: {
|
||||
clientId: string;
|
||||
/** secret 자체는 client 에 echo 안 함 — *설정 여부* 만. true 면 input placeholder 가 "저장됨" 으로 바뀜. */
|
||||
hasClientSecret: boolean;
|
||||
calendarId: string;
|
||||
defaultEventDurationMinutes: number;
|
||||
/** iCal URL 도 capability 토큰성이라 *설정 여부* 만 전송. 사용자가 새 값 입력 시 덮어씀. */
|
||||
hasIcalUrl: boolean;
|
||||
icalDaysAhead: number;
|
||||
/** OAuth 연결 상태 — globalState 의 refresh token 존재 여부 + 누가 연결됐는지. */
|
||||
connected: boolean;
|
||||
connectedAs?: string;
|
||||
connectedAt?: string;
|
||||
lastIcalFetchAt?: string;
|
||||
};
|
||||
/** Sectional banner shown when config.update fails (e.g. reload required). */
|
||||
bannerError?: string;
|
||||
}
|
||||
@@ -208,6 +225,19 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
||||
case 'advanced.update':
|
||||
await this._handleAdvancedUpdate(msg);
|
||||
return;
|
||||
case 'google.update':
|
||||
await this._handleGoogleUpdate(msg);
|
||||
return;
|
||||
case 'google.connect':
|
||||
await vscode.commands.executeCommand('g1nation.calendar.connectOAuth');
|
||||
await this._refreshState();
|
||||
return;
|
||||
case 'google.disconnect':
|
||||
await this._handleGoogleDisconnect();
|
||||
return;
|
||||
case 'google.icalRefresh':
|
||||
await this._handleGoogleIcalRefresh();
|
||||
return;
|
||||
case 'openVscodeSettings':
|
||||
await vscode.commands.executeCommand('workbench.action.openSettings', 'g1nation');
|
||||
return;
|
||||
@@ -403,6 +433,82 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
}
|
||||
|
||||
// ────────────── Google (Calendar + Sheets) ──────────────
|
||||
// Settings 패널이 보여주는 모든 Google 필드는 `g1nation.google.*` configuration 으로
|
||||
// 저장. secret(Client Secret / iCal URL / refresh token)은 *값 자체를 client 에 echo
|
||||
// 안 함* — 설정 여부만 true/false 로. 사용자가 새 값을 입력 시 덮어쓰는 단방향.
|
||||
|
||||
private _buildGoogleState(): SettingsState['google'] {
|
||||
const ctx = this._deps.context;
|
||||
const { readCalendarConfig } = require('../calendar') as typeof import('../calendar');
|
||||
const cur = readCalendarConfig(ctx);
|
||||
return {
|
||||
clientId: cur.clientId ?? '',
|
||||
hasClientSecret: !!cur.clientSecret,
|
||||
calendarId: cur.calendarId ?? 'primary',
|
||||
defaultEventDurationMinutes: cur.defaultDurationMinutes ?? 60,
|
||||
hasIcalUrl: !!cur.icalUrl,
|
||||
icalDaysAhead: cur.daysAhead ?? 14,
|
||||
connected: !!cur.refreshToken,
|
||||
connectedAs: cur.connectedAs,
|
||||
connectedAt: cur.connectedAt,
|
||||
lastIcalFetchAt: cur.lastFetchAt,
|
||||
};
|
||||
}
|
||||
|
||||
private async _handleGoogleUpdate(msg: any): Promise<void> {
|
||||
const { writeCalendarConfig } = require('../calendar') as typeof import('../calendar');
|
||||
// 필드별로 *명시적으로 전달된 것만* 패치. undefined / 누락은 무시.
|
||||
const patch: any = {};
|
||||
if (typeof msg.clientId === 'string') patch.clientId = msg.clientId.trim() || undefined;
|
||||
if (typeof msg.clientSecret === 'string') patch.clientSecret = msg.clientSecret.trim() || undefined;
|
||||
if (typeof msg.calendarId === 'string') patch.calendarId = msg.calendarId.trim() || 'primary';
|
||||
if (typeof msg.defaultEventDurationMinutes === 'number' && Number.isFinite(msg.defaultEventDurationMinutes)) {
|
||||
patch.defaultDurationMinutes = Math.max(5, Math.min(720, Math.floor(msg.defaultEventDurationMinutes)));
|
||||
}
|
||||
if (typeof msg.icalUrl === 'string') patch.icalUrl = msg.icalUrl.trim();
|
||||
if (typeof msg.icalDaysAhead === 'number' && Number.isFinite(msg.icalDaysAhead)) {
|
||||
patch.daysAhead = Math.max(1, Math.min(90, Math.floor(msg.icalDaysAhead)));
|
||||
}
|
||||
if (Object.keys(patch).length === 0) return;
|
||||
await writeCalendarConfig(this._deps.context, patch);
|
||||
this._lastSuccess = '저장되었습니다.';
|
||||
this._lastError = undefined;
|
||||
await this._refreshState();
|
||||
}
|
||||
|
||||
private async _handleGoogleDisconnect(): Promise<void> {
|
||||
const { writeCalendarConfig } = require('../calendar') as typeof import('../calendar');
|
||||
await writeCalendarConfig(this._deps.context, {
|
||||
refreshToken: undefined,
|
||||
accessToken: undefined,
|
||||
accessTokenExpiresAt: undefined,
|
||||
connectedAs: undefined,
|
||||
connectedAt: undefined,
|
||||
});
|
||||
this._lastSuccess = 'OAuth 연결을 해제했습니다. https://myaccount.google.com/permissions 에서 권한 회수 권장.';
|
||||
this._lastError = undefined;
|
||||
await this._refreshState();
|
||||
}
|
||||
|
||||
private async _handleGoogleIcalRefresh(): Promise<void> {
|
||||
try {
|
||||
const { refreshCalendarCache } = require('../calendar') as typeof import('../calendar');
|
||||
const r = await refreshCalendarCache(this._deps.context);
|
||||
if (r.ok) {
|
||||
this._lastSuccess = `iCal 새로고침 완료 — ${r.count}개 일정 동기화`;
|
||||
this._lastError = undefined;
|
||||
} else {
|
||||
this._lastError = r.error || 'iCal 새로고침 실패';
|
||||
this._lastSuccess = undefined;
|
||||
}
|
||||
} catch (e: any) {
|
||||
this._lastError = e?.message ?? String(e);
|
||||
this._lastSuccess = undefined;
|
||||
}
|
||||
await this._refreshState();
|
||||
}
|
||||
|
||||
private async _handleAdvancedUpdate(msg: any): Promise<void> {
|
||||
if (typeof msg.dryRun === 'boolean') {
|
||||
await this._safeConfigUpdate('dryRun', msg.dryRun);
|
||||
@@ -466,6 +572,7 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
||||
maxAutoSteps: cfg.get<number>('maxAutoSteps', 50) ?? 50,
|
||||
maxContextSize: cfg.get<number>('maxContextSize', 32000) ?? 32000,
|
||||
},
|
||||
google: this._buildGoogleState(),
|
||||
bannerError: this._bannerError,
|
||||
};
|
||||
const payload = { type: 'state', value: state };
|
||||
|
||||
Reference in New Issue
Block a user