Move calendar users from kvs to local (#9631)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2025-08-01 08:46:00 +07:00
committed by GitHub
parent d32beb8ead
commit 3fe325bce0
9 changed files with 46 additions and 65 deletions
+4 -4
View File
@@ -31,11 +31,11 @@ import { Credentials, OAuth2Client } from 'google-auth-library'
import { calendar_v3, google } from 'googleapis'
import { encode64 } from './base64'
import { getClient } from './client'
import { addUserByEmail, removeUserByEmail, setSyncHistory } from './kvsUtils'
import { setSyncHistory } from './kvsUtils'
import { lock } from './mutex'
import { IncomingSyncManager } from './sync'
import { CALENDAR_INTEGRATION, GoogleEmail, SCOPES, State, Token, User } from './types'
import { getGoogleClient, removeIntegrationSecret } from './utils'
import { addUserByEmail, getGoogleClient, removeIntegrationSecret, removeUserByEmail } from './utils'
import { WatchController } from './watch'
interface AuthResult {
@@ -122,7 +122,7 @@ export class AuthController {
if (integration !== undefined) {
await this.client.remove(integration)
}
await removeUserByEmail(this.user, value)
removeUserByEmail(this.user, value)
const data = {
kind: CALENDAR_INTEGRATION,
workspaceUuid: this.user.workspace,
@@ -283,7 +283,7 @@ export class AuthController {
} else {
await this.accountClient.addIntegrationSecret(data)
}
await addUserByEmail(_token, email)
addUserByEmail(_token, email)
} catch (err) {
this.ctx.error('update token error', { workspace: this.user.workspace, user: this.user.userId, err })
}
@@ -20,7 +20,6 @@ import { areEqualMarkups, htmlToMarkup, isEmptyMarkup, jsonToHTML, markupToJSON
import { deepEqual } from 'fast-equals'
import { OAuth2Client } from 'google-auth-library'
import { calendar_v3 } from 'googleapis'
import { removeUserByEmail } from './kvsUtils'
import { getRateLimitter, RateLimiter } from './rateLimiter'
import { CALENDAR_INTEGRATION, type Token } from './types'
import {
@@ -32,6 +31,7 @@ import {
parseEventDate,
parseRecurrenceStrings,
removeIntegrationSecret,
removeUserByEmail,
setCredentials
} from './utils'
import type { WorkspaceClient } from './workspaceClient'
@@ -66,7 +66,7 @@ export class CalendarClient {
const calendarClient = new CalendarClient(ctx, accountClient, user, client, workspace)
const authSucces = await setCredentials(calendarClient.oAuth2Client, user)
if (!authSucces) {
await removeUserByEmail(user, user.email)
removeUserByEmail(user, user.email)
await removeIntegrationSecret(ctx, calendarClient.accountClient, {
socialId: user.userId,
kind: CALENDAR_INTEGRATION,
@@ -25,7 +25,6 @@ import {
import config from './config'
import { getIntegrations } from './integrations'
import { WorkspaceClient } from './workspaceClient'
import { cleanUserByEmail } from './kvsUtils'
interface WorkspaceStateInfo {
shouldStart: boolean
@@ -72,7 +71,6 @@ export class CalendarController {
}
private async runAll (groups: Map<WorkspaceUuid, Integration[]>): Promise<void> {
await cleanUserByEmail()
const ids = [...groups.keys()]
if (ids.length === 0) return
const limiter = new RateLimiter(config.InitLimit)
+1 -40
View File
@@ -1,7 +1,7 @@
import { WorkspaceUuid } from '@hcengineering/core'
import { KeyValueClient, getClient as getKeyValueClient } from '@hcengineering/kvs-client'
import config from './config'
import { CALENDAR_INTEGRATION, GoogleEmail, Token, User } from './types'
import { CALENDAR_INTEGRATION, GoogleEmail, User } from './types'
import { getServiceToken } from './utils'
let keyValueClient: KeyValueClient | undefined
@@ -59,42 +59,3 @@ export async function setEventHistory (
const client = getKvsClient()
await client.setValue(eventHistoryKey(user, email, calendarId), historyId)
}
export async function getUserByEmail (email: GoogleEmail): Promise<Token[]> {
const client = getKvsClient()
const key = `${CALENDAR_INTEGRATION}:users:${email}`
return (await client.getValue<Token[]>(key)) ?? []
}
export async function addUserByEmail (user: Token, email: GoogleEmail): Promise<void> {
const client = getKvsClient()
const key = `${CALENDAR_INTEGRATION}:users:${email}`
const curr = (await client.getValue<Token[]>(key)) ?? []
const exists = curr.find((p) => p.userId === user.userId && p.workspace === user.workspace)
if (exists !== undefined) {
return
}
curr.push(user)
await client.setValue<Token[]>(key, curr)
}
export async function removeUserByEmail (user: User, email: GoogleEmail): Promise<void> {
const client = getKvsClient()
const key = `${CALENDAR_INTEGRATION}:users:${email}`
const curr = (await client.getValue<User[]>(key)) ?? []
const newCurr = curr.filter((p) => p.userId !== user.userId || p.workspace !== user.workspace)
if (newCurr.length === 0) {
await client.deleteKey(key)
} else {
await client.setValue<User[]>(key, newCurr)
}
}
export async function cleanUserByEmail (): Promise<void> {
const client = getKvsClient()
const keys = await client.listKeys(`${CALENDAR_INTEGRATION}:users:`)
if (keys?.keys == null) return
for (const key of keys.keys) {
await client.deleteKey(key)
}
}
@@ -22,7 +22,7 @@ import { deepEqual } from 'fast-equals'
import { OAuth2Client } from 'google-auth-library'
import { calendar_v3 } from 'googleapis'
import { getClient } from './client'
import { removeUserByEmail, setSyncHistory } from './kvsUtils'
import { setSyncHistory } from './kvsUtils'
import { lock, synced } from './mutex'
import { getRateLimitter, RateLimiter } from './rateLimiter'
import { CALENDAR_INTEGRATION, Token } from './types'
@@ -35,6 +35,7 @@ import {
parseEventDate,
parseRecurrenceStrings,
removeIntegrationSecret,
removeUserByEmail,
setCredentials
} from './utils'
@@ -417,7 +418,7 @@ export class OutcomingClient {
workspace: user.workspace,
email: user.email
})
await removeUserByEmail(user, user.email)
removeUserByEmail(user, user.email)
await removeIntegrationSecret(ctx, accountClient, {
socialId: user.userId,
kind: CALENDAR_INTEGRATION,
@@ -16,10 +16,9 @@
import { AccountClient } from '@hcengineering/account-client'
import { MeasureContext, TxOperations } from '@hcengineering/core'
import { getClient } from './client'
import { getUserByEmail, removeUserByEmail } from './kvsUtils'
import { IncomingSyncManager } from './sync'
import { CALENDAR_INTEGRATION, GoogleEmail, Token } from './types'
import { getGoogleClient, removeIntegrationSecret, setCredentials } from './utils'
import { getGoogleClient, getUserByEmail, removeIntegrationSecret, removeUserByEmail, setCredentials } from './utils'
export class PushHandler {
constructor (
@@ -37,7 +36,7 @@ export class PushHandler {
const res = getGoogleClient()
const authSuccess = await setCredentials(res.auth, token)
if (!authSuccess) {
await removeUserByEmail(token, token.email)
removeUserByEmail(token, token.email)
await removeIntegrationSecret(this.ctx, this.accountClient, {
kind: CALENDAR_INTEGRATION,
workspaceUuid: token.workspace,
@@ -63,7 +62,7 @@ export class PushHandler {
}
async push (email: GoogleEmail, mode: 'events' | 'calendar', calendarId?: string): Promise<void> {
const tokens = await getUserByEmail(email)
const tokens = getUserByEmail(email)
this.ctx.info('push', { email, mode, calendarId, tokens: tokens.length })
for (const token of tokens) {
await this.sync(token, mode === 'events' ? calendarId ?? null : null)
+3 -8
View File
@@ -43,13 +43,7 @@ import { htmlToMarkup } from '@hcengineering/text'
import { deepEqual } from 'fast-equals'
import { calendar_v3 } from 'googleapis'
import { getClient } from './client'
import {
getCalendarsSyncHistory,
getEventHistory,
removeUserByEmail,
setCalendarsSyncHistory,
setEventHistory
} from './kvsUtils'
import { getCalendarsSyncHistory, getEventHistory, setCalendarsSyncHistory, setEventHistory } from './kvsUtils'
import { lock } from './mutex'
import { getRateLimitter, RateLimiter } from './rateLimiter'
import { CALENDAR_INTEGRATION, GoogleEmail, Token, User } from './types'
@@ -58,6 +52,7 @@ import {
parseEventDate,
parseRecurrenceStrings,
removeIntegrationSecret,
removeUserByEmail,
setCredentials
} from './utils'
import { WatchController } from './watch'
@@ -104,7 +99,7 @@ export class IncomingSyncManager {
try {
const authSucces = await setCredentials(google.auth, user)
if (!authSucces) {
await removeUserByEmail(user, user.email)
removeUserByEmail(user, user.email)
await removeIntegrationSecret(ctx, accountClient, {
socialId: user.userId,
kind: CALENDAR_INTEGRATION,
+27 -1
View File
@@ -29,7 +29,7 @@ import { generateToken } from '@hcengineering/server-token'
import { Credentials, OAuth2Client } from 'google-auth-library'
import { calendar_v3, google } from 'googleapis'
import config from './config'
import { CALENDAR_INTEGRATION, ReccuringData, State, type Token, type User } from './types'
import { CALENDAR_INTEGRATION, GoogleEmail, ReccuringData, State, type Token, type User } from './types'
export class DeferredPromise<T = any> {
public readonly promise: Promise<T>
@@ -363,3 +363,29 @@ export function parseEventDate (date: calendar_v3.Schema$EventDateTime | undefin
}
return 0
}
const users = new Map<GoogleEmail, Token[]>()
export function getUserByEmail (email: GoogleEmail): Token[] {
return users.get(email) ?? []
}
export function addUserByEmail (user: Token, email: GoogleEmail): void {
const curr = getUserByEmail(email)
const exists = curr.find((p) => p.userId === user.userId && p.workspace === user.workspace)
if (exists !== undefined) {
return
}
curr.push(user)
users.set(email, curr)
}
export function removeUserByEmail (user: User, email: GoogleEmail): void {
const curr = getUserByEmail(email)
const newCurr = curr.filter((p) => p.userId !== user.userId || p.workspace !== user.workspace)
if (newCurr.length === 0) {
users.delete(email)
} else {
users.set(email, newCurr)
}
}
@@ -27,11 +27,12 @@ import core, {
import { CalendarClient } from './calendar'
import { getClient } from './client'
import config from './config'
import { addUserByEmail, getSyncHistory, setSyncHistory } from './kvsUtils'
import { getSyncHistory, setSyncHistory } from './kvsUtils'
import { synced } from './mutex'
import { IncomingSyncManager } from './sync'
import { getWorkspaceTokens } from './tokens'
import { GoogleEmail, Token } from './types'
import { addUserByEmail } from './utils'
export class WorkspaceClient {
private readonly clients = new Map<GoogleEmail, CalendarClient>()
@@ -89,7 +90,7 @@ export class WorkspaceClient {
for (const token of tokens) {
if (token.workspaceUuid === null) continue
const parsedToken = JSON.parse(token.secret)
await addUserByEmail(parsedToken, token.key as GoogleEmail)
addUserByEmail(parsedToken, token.key as GoogleEmail)
await this.createCalendarClient(parsedToken)
}
const limiter = new RateLimiter(config.InitLimit)