mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-11 20:27:43 +02:00
UBERF-8425: Global accounts (#7573)
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { CollaboratorClient } from '@hcengineering/collaborator-client'
|
||||
import contact, { AvatarType, Person, PersonAccount } from '@hcengineering/contact'
|
||||
import contact, { AvatarType, Person } from '@hcengineering/contact'
|
||||
import core, {
|
||||
Account,
|
||||
PersonId,
|
||||
AccountRole,
|
||||
AttachedDoc,
|
||||
Branding,
|
||||
@@ -28,7 +29,7 @@ import core, {
|
||||
TxWorkspaceEvent,
|
||||
WithLookup,
|
||||
WorkspaceEvent,
|
||||
WorkspaceIdWithUrl,
|
||||
WorkspaceUuid,
|
||||
concatLink,
|
||||
generateId,
|
||||
groupByArray,
|
||||
@@ -36,7 +37,9 @@ import core, {
|
||||
toIdMap,
|
||||
type Blob,
|
||||
type MigrationState,
|
||||
type TimeRateLimiter
|
||||
type TimeRateLimiter,
|
||||
type WorkspaceIds,
|
||||
type WorkspaceDataId
|
||||
} from '@hcengineering/core'
|
||||
import github, {
|
||||
DocSyncInfo,
|
||||
@@ -86,6 +89,10 @@ import {
|
||||
githubSyncVersion
|
||||
} from './types'
|
||||
import { equalExceptKeys } from './utils'
|
||||
|
||||
// TODO: FIXME
|
||||
type PersonAccount = any
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -97,7 +104,8 @@ export class GithubWorker implements IntegrationManager {
|
||||
|
||||
triggerRequests: number = 0
|
||||
|
||||
authRequestSend = new Set<Ref<Account>>()
|
||||
// TODO: FIXME
|
||||
authRequestSend = new Set<any>()
|
||||
|
||||
triggerSync: () => void = () => {
|
||||
this.triggerRequests++
|
||||
@@ -128,11 +136,11 @@ export class GithubWorker implements IntegrationManager {
|
||||
clearInterval(this.periodicTimer)
|
||||
|
||||
this.closing = true
|
||||
this.ctx.warn('Closing', { workspace: this.workspace.name })
|
||||
this.ctx.warn('Closing', { workspace: this.workspace })
|
||||
this.triggerSync()
|
||||
await Promise.all([await this.syncPromise, new Promise<void>((resolve) => setTimeout(resolve, 5000))])
|
||||
|
||||
this.ctx.warn('Closing Done', { workspace: this.workspace.name })
|
||||
this.ctx.warn('Closing Done', { workspace: this.workspace })
|
||||
await this.client.close()
|
||||
}
|
||||
|
||||
@@ -140,8 +148,12 @@ export class GithubWorker implements IntegrationManager {
|
||||
await this.liveQuery.refreshConnect(clean)
|
||||
}
|
||||
|
||||
getWorkspaceId (): WorkspaceIdWithUrl {
|
||||
return this.workspace
|
||||
getWorkspaceId (): WorkspaceUuid {
|
||||
return this.workspace.uuid
|
||||
}
|
||||
|
||||
getWorkspaceUrl (): string {
|
||||
return this.workspace.url
|
||||
}
|
||||
|
||||
getBranding (): Branding | null {
|
||||
@@ -174,9 +186,9 @@ export class GithubWorker implements IntegrationManager {
|
||||
return ''
|
||||
}
|
||||
const frontUrl = this.getBranding()?.front ?? config.FrontURL
|
||||
const refUrl = concatLink(frontUrl, `/browse/?workspace=${this.workspace.name}`)
|
||||
const refUrl = concatLink(frontUrl, `/browse/?workspace=${this.workspace.uuid}`)
|
||||
// TODO storage URL
|
||||
const imageUrl = concatLink(frontUrl ?? config.FrontURL, `/files?workspace=${this.workspace.name}&file=`)
|
||||
const imageUrl = concatLink(frontUrl ?? config.FrontURL, `/files?workspace=${this.workspace.uuid}&file=`)
|
||||
const guestUrl = getPublicLinkUrl(this.workspace, frontUrl)
|
||||
const json = parseMessageMarkdown(text ?? '', refUrl, imageUrl, guestUrl)
|
||||
await preprocessor?.(json)
|
||||
@@ -189,9 +201,9 @@ export class GithubWorker implements IntegrationManager {
|
||||
}
|
||||
return await markupToMarkdown(
|
||||
text ?? '',
|
||||
concatLink(this.getBranding()?.front ?? config.FrontURL, `/browse/?workspace=${this.workspace.name}`),
|
||||
concatLink(this.getBranding()?.front ?? config.FrontURL, `/browse/?workspace=${this.workspace.uuid}`),
|
||||
// TODO storage URL
|
||||
concatLink(this.getBranding()?.front ?? config.FrontURL, `/files/${this.workspace.name}/`),
|
||||
concatLink(this.getBranding()?.front ?? config.FrontURL, `/files/${this.workspace.uuid}/`),
|
||||
preprocessor
|
||||
)
|
||||
}
|
||||
@@ -251,79 +263,81 @@ export class GithubWorker implements IntegrationManager {
|
||||
}
|
||||
|
||||
async _getAccountRaw (userInfo?: UserInfo | null): Promise<PersonAccount | undefined> {
|
||||
// We need to sync by userInfo id to prevent parallel requests.
|
||||
if (userInfo === null) {
|
||||
// Ghost author.
|
||||
return await this.getAccount({
|
||||
id: 'ghost',
|
||||
login: 'ghost',
|
||||
avatarUrl: 'https://avatars.githubusercontent.com/u/10137?v=4',
|
||||
email: '<EMAIL>',
|
||||
name: 'Ghost'
|
||||
})
|
||||
}
|
||||
if (userInfo?.login == null) {
|
||||
return
|
||||
}
|
||||
const userName = (userInfo.name ?? userInfo.login)
|
||||
.split(' ')
|
||||
.map((it) => it.trim())
|
||||
.reverse()
|
||||
.join(',') // TODO: Convert first, last name
|
||||
// TODO: FIXME
|
||||
throw new Error('Not implemented')
|
||||
// // We need to sync by userInfo id to prevent parallel requests.
|
||||
// if (userInfo === null) {
|
||||
// // Ghost author.
|
||||
// return await this.getAccount({
|
||||
// id: 'ghost',
|
||||
// login: 'ghost',
|
||||
// avatarUrl: 'https://avatars.githubusercontent.com/u/10137?v=4',
|
||||
// email: '<EMAIL>',
|
||||
// name: 'Ghost'
|
||||
// })
|
||||
// }
|
||||
// if (userInfo?.login == null) {
|
||||
// return
|
||||
// }
|
||||
// const userName = (userInfo.name ?? userInfo.login)
|
||||
// .split(' ')
|
||||
// .map((it) => it.trim())
|
||||
// .reverse()
|
||||
// .join(',') // TODO: Convert first, last name
|
||||
|
||||
const infos = await this.liveQuery.findOne(github.class.GithubUserInfo, { login: userInfo.login })
|
||||
if (infos === undefined) {
|
||||
await this._client.createDoc(github.class.GithubUserInfo, contact.space.Contacts, {
|
||||
...userInfo
|
||||
})
|
||||
}
|
||||
// const infos = await this.liveQuery.findOne(github.class.GithubUserInfo, { login: userInfo.login })
|
||||
// if (infos === undefined) {
|
||||
// await this._client.createDoc(github.class.GithubUserInfo, contact.space.Contacts, {
|
||||
// ...userInfo
|
||||
// })
|
||||
// }
|
||||
|
||||
const account = await this.client
|
||||
.getModel()
|
||||
.findOne(contact.class.PersonAccount, { email: `github:${userInfo.login}` })
|
||||
if (account !== undefined) {
|
||||
const person = await this.liveQuery.findOne(contact.class.Person, { _id: account.person })
|
||||
// We need to be sure employee are exists.
|
||||
if (person === undefined) {
|
||||
const person: Ref<Person> = await this.findPerson(userInfo, userName)
|
||||
if (account.person !== person) {
|
||||
await this._client.update(account, { person })
|
||||
}
|
||||
}
|
||||
return account
|
||||
} else {
|
||||
// Check authorized users
|
||||
const accountRecord = await this.platform.getAccount(userInfo.login)
|
||||
if (accountRecord !== undefined) {
|
||||
const authorizedId = accountRecord.accounts[this.workspace.name]
|
||||
if (authorizedId !== undefined) {
|
||||
const emp = await this._client.findOne(contact.class.PersonAccount, {
|
||||
_id: authorizedId as Ref<PersonAccount>
|
||||
})
|
||||
if (emp !== undefined) {
|
||||
// We need to create github account
|
||||
const gid = await this._client.createDoc(contact.class.PersonAccount, core.space.Model, {
|
||||
email: `github:${userInfo.login}`,
|
||||
person: emp.person,
|
||||
role: AccountRole.User
|
||||
})
|
||||
const acc = await this._client.findOne(contact.class.PersonAccount, { _id: gid })
|
||||
return acc
|
||||
}
|
||||
}
|
||||
}
|
||||
// const account = await this.client
|
||||
// .getModel()
|
||||
// .findOne(contact.class.PersonAccount, { email: `github:${userInfo.login}` })
|
||||
// if (account !== undefined) {
|
||||
// const person = await this.liveQuery.findOne(contact.class.Person, { _id: account.person })
|
||||
// // We need to be sure employee are exists.
|
||||
// if (person === undefined) {
|
||||
// const person: Ref<Person> = await this.findPerson(userInfo, userName)
|
||||
// if (account.person !== person) {
|
||||
// await this._client.update(account, { person })
|
||||
// }
|
||||
// }
|
||||
// return account
|
||||
// } else {
|
||||
// // Check authorized users
|
||||
// const accountRecord = await this.platform.getAccount(userInfo.login)
|
||||
// if (accountRecord !== undefined) {
|
||||
// const authorizedId = accountRecord.accounts[this.workspace.name]
|
||||
// if (authorizedId !== undefined) {
|
||||
// const emp = await this._client.findOne(contact.class.PersonAccount, {
|
||||
// _id: authorizedId as Ref<PersonAccount>
|
||||
// })
|
||||
// if (emp !== undefined) {
|
||||
// // We need to create github account
|
||||
// const gid = await this._client.createDoc(contact.class.PersonAccount, core.space.Model, {
|
||||
// email: `github:${userInfo.login}`,
|
||||
// person: emp.person,
|
||||
// role: AccountRole.User
|
||||
// })
|
||||
// const acc = await this._client.findOne(contact.class.PersonAccount, { _id: gid })
|
||||
// return acc
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
const person: Ref<Person> | undefined = await this.findPerson(userInfo, userName)
|
||||
// const person: Ref<Person> | undefined = await this.findPerson(userInfo, userName)
|
||||
|
||||
// We need to create email account
|
||||
const id = await this._client.createDoc(contact.class.PersonAccount, core.space.Model, {
|
||||
email: `github:${userInfo.login}`,
|
||||
person,
|
||||
role: AccountRole.User
|
||||
})
|
||||
const acc = await this.client.getModel().findOne(contact.class.PersonAccount, { _id: id })
|
||||
return acc
|
||||
}
|
||||
// // We need to create email account
|
||||
// const id = await this._client.createDoc(contact.class.PersonAccount, core.space.Model, {
|
||||
// email: `github:${userInfo.login}`,
|
||||
// person,
|
||||
// role: AccountRole.User
|
||||
// })
|
||||
// const acc = await this.client.getModel().findOne(contact.class.PersonAccount, { _id: id })
|
||||
// return acc
|
||||
// }
|
||||
}
|
||||
|
||||
private constructor (
|
||||
@@ -334,7 +348,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
readonly client: Client,
|
||||
readonly app: App,
|
||||
readonly storageAdapter: StorageAdapter,
|
||||
readonly workspace: WorkspaceIdWithUrl,
|
||||
readonly workspace: WorkspaceIds,
|
||||
readonly branding: Branding | null,
|
||||
readonly periodicSyncInterval = 60 * 60 * 1000
|
||||
) {
|
||||
@@ -343,7 +357,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
|
||||
this.repositoryManager = new RepositorySyncMapper(this.ctx.newChild('repository', {}), this._client, this.app)
|
||||
|
||||
this.collaborator = createCollaboratorClient(this.workspace)
|
||||
this.collaborator = createCollaboratorClient(this.workspace.uuid)
|
||||
|
||||
this.personMapper = new UsersSyncManager(this.ctx.newChild('users', {}), this._client, this.liveQuery)
|
||||
|
||||
@@ -370,10 +384,11 @@ export class GithubWorker implements IntegrationManager {
|
||||
_class: [chunter.class.ChatMessage],
|
||||
mapper: new CommentSyncManager(this.ctx.newChild('comment', {}), this._client, this.liveQuery)
|
||||
},
|
||||
{
|
||||
_class: [contact.class.PersonAccount],
|
||||
mapper: this.personMapper
|
||||
},
|
||||
// TODO: FIXME
|
||||
// {
|
||||
// _class: [contact.class.PersonAccount],
|
||||
// mapper: this.personMapper
|
||||
// },
|
||||
{
|
||||
_class: [github.class.GithubReview],
|
||||
mapper: new ReviewSyncManager(this.ctx.newChild('review', {}), this._client, this.liveQuery)
|
||||
@@ -407,219 +422,228 @@ export class GithubWorker implements IntegrationManager {
|
||||
this.periodicSyncPromise = undefined
|
||||
}
|
||||
|
||||
private async findPerson (userInfo: UserInfo, userName: string): Promise<Ref<Person>> {
|
||||
let person: Ref<Person> | undefined
|
||||
// try to find by account.
|
||||
if (userInfo.email != null && userInfo.email.trim().length > 0) {
|
||||
const personAccount = await this.client.getModel().findOne(contact.class.PersonAccount, { email: userInfo.email })
|
||||
person = personAccount?.person
|
||||
}
|
||||
// private async findPerson (userInfo: UserInfo, userName: string): Promise<Ref<Person>> {
|
||||
// let person: Ref<Person> | undefined
|
||||
// // try to find by account.
|
||||
// if (userInfo.email != null && userInfo.email.trim().length > 0) {
|
||||
// const personAccount = await this.client.getModel().findOne(contact.class.PersonAccount, { email: userInfo.email })
|
||||
// person = personAccount?.person
|
||||
// }
|
||||
|
||||
if (person === undefined) {
|
||||
const channel = await this.liveQuery.findOne(contact.class.Channel, {
|
||||
provider: contact.channelProvider.GitHub,
|
||||
value: userInfo.login
|
||||
})
|
||||
person = channel?.attachedTo as Ref<Person>
|
||||
}
|
||||
// if (person === undefined) {
|
||||
// const channel = await this.liveQuery.findOne(contact.class.Channel, {
|
||||
// provider: contact.channelProvider.GitHub,
|
||||
// value: userInfo.login
|
||||
// })
|
||||
// person = channel?.attachedTo as Ref<Person>
|
||||
// }
|
||||
|
||||
if (person === undefined) {
|
||||
// We need to create some person to identify this account.
|
||||
person = await this._client.createDoc(contact.class.Person, contact.space.Contacts, {
|
||||
name: userName,
|
||||
avatarType: AvatarType.EXTERNAL,
|
||||
avatarProps: { url: userInfo.avatarUrl },
|
||||
city: '',
|
||||
comments: 0,
|
||||
channels: 0,
|
||||
attachments: 0
|
||||
})
|
||||
await this._client.addCollection(
|
||||
contact.class.Channel,
|
||||
contact.space.Contacts,
|
||||
person,
|
||||
contact.class.Person,
|
||||
'channels',
|
||||
{
|
||||
provider: contact.channelProvider.GitHub,
|
||||
value: userInfo.login
|
||||
}
|
||||
)
|
||||
if (userInfo.email != null && userInfo.email.trim() !== '') {
|
||||
await this._client.addCollection(
|
||||
contact.class.Channel,
|
||||
contact.space.Contacts,
|
||||
person,
|
||||
contact.class.Person,
|
||||
'channels',
|
||||
{
|
||||
provider: contact.channelProvider.Email,
|
||||
value: userInfo.email
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
return person
|
||||
}
|
||||
// if (person === undefined) {
|
||||
// // We need to create some person to identify this account.
|
||||
// person = await this._client.createDoc(contact.class.Person, contact.space.Contacts, {
|
||||
// name: userName,
|
||||
// avatarType: AvatarType.EXTERNAL,
|
||||
// avatarProps: { url: userInfo.avatarUrl },
|
||||
// city: '',
|
||||
// comments: 0,
|
||||
// channels: 0,
|
||||
// attachments: 0
|
||||
// })
|
||||
// await this._client.addCollection(
|
||||
// contact.class.Channel,
|
||||
// contact.space.Contacts,
|
||||
// person,
|
||||
// contact.class.Person,
|
||||
// 'channels',
|
||||
// {
|
||||
// provider: contact.channelProvider.GitHub,
|
||||
// value: userInfo.login
|
||||
// }
|
||||
// )
|
||||
// if (userInfo.email != null && userInfo.email.trim() !== '') {
|
||||
// await this._client.addCollection(
|
||||
// contact.class.Channel,
|
||||
// contact.space.Contacts,
|
||||
// person,
|
||||
// contact.class.Person,
|
||||
// 'channels',
|
||||
// {
|
||||
// provider: contact.channelProvider.Email,
|
||||
// value: userInfo.email
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// return person
|
||||
// }
|
||||
|
||||
async getGithubLogin (container: IntegrationContainer, person: Ref<Person>): Promise<UserInfo | undefined> {
|
||||
const accounts = this.client.getModel().findAllSync(contact.class.PersonAccount, {})
|
||||
const acc = accounts.find((it) => it.person === person && it.email.startsWith('github:'))
|
||||
if (acc === undefined) {
|
||||
return // Nobody, will use system account.
|
||||
}
|
||||
const login = acc.email.substring(7)
|
||||
let info = await this.liveQuery.findOne(github.class.GithubUserInfo, { login })
|
||||
if (info === undefined) {
|
||||
// We need to retrieve info for login
|
||||
const response: any = await container.octokit?.graphql(
|
||||
`query($login: String!) {
|
||||
user(login: $login) {
|
||||
id
|
||||
email
|
||||
login
|
||||
name
|
||||
avatarUrl
|
||||
}
|
||||
}`,
|
||||
{
|
||||
login
|
||||
}
|
||||
)
|
||||
|
||||
info = response.user
|
||||
await this._client.createDoc(github.class.GithubUserInfo, contact.space.Contacts, info as Data<GithubUserInfo>)
|
||||
}
|
||||
return info
|
||||
// TODO: FIXME
|
||||
throw new Error('Not implemented')
|
||||
// const accounts = this.client.getModel().findAllSync(contact.class.PersonAccount, {})
|
||||
// const acc = accounts.find((it) => it.person === person && it.email.startsWith('github:'))
|
||||
// if (acc === undefined) {
|
||||
// return // Nobody, will use system account.
|
||||
// }
|
||||
// const login = acc.email.substring(7)
|
||||
// let info = await this.liveQuery.findOne(github.class.GithubUserInfo, { login })
|
||||
// if (info === undefined) {
|
||||
// // We need to retrieve info for login
|
||||
// const response: any = await container.octokit?.graphql(
|
||||
// `query($login: String!) {
|
||||
// user(login: $login) {
|
||||
// id
|
||||
// email
|
||||
// login
|
||||
// name
|
||||
// avatarUrl
|
||||
// }
|
||||
// }`,
|
||||
// {
|
||||
// login
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// info = response.user
|
||||
// await this._client.createDoc(github.class.GithubUserInfo, contact.space.Contacts, info as Data<GithubUserInfo>)
|
||||
// }
|
||||
// return info
|
||||
}
|
||||
|
||||
async syncUserData (ctx: MeasureContext, users: GithubUserRecord[]): Promise<void> {
|
||||
// TODO: FIXME
|
||||
throw new Error('Not implemented')
|
||||
// Let's sync information about users and send some details
|
||||
const accounts = await this._client.findAll(contact.class.PersonAccount, {
|
||||
email: { $in: users.map((it) => `github:${it._id}`) }
|
||||
})
|
||||
const userAuths = await this._client.findAll(github.class.GithubAuthentication, {})
|
||||
const persons = await this._client.findAll(contact.class.Person, { _id: { $in: accounts.map((it) => it.person) } })
|
||||
for (const record of users) {
|
||||
if (record.error !== undefined) {
|
||||
// Skip accounts with error
|
||||
continue
|
||||
}
|
||||
const account = accounts.find((it) => it.email === `github:${record._id}`)
|
||||
const userAuth = userAuths.find((it) => it.login === record._id)
|
||||
const person = persons.find((it) => account?.person)
|
||||
if (account === undefined || userAuth === undefined || person === undefined) {
|
||||
continue
|
||||
}
|
||||
const accountRef = record.accounts[this.workspace.name]
|
||||
try {
|
||||
await this.platform.checkRefreshToken(record, true)
|
||||
// const accounts = await this._client.findAll(contact.class.PersonAccount, {
|
||||
// email: { $in: users.map((it) => `github:${it._id}`) }
|
||||
// })
|
||||
// const userAuths = await this._client.findAll(github.class.GithubAuthentication, {})
|
||||
// const persons = await this._client.findAll(contact.class.Person, { _id: { $in: accounts.map((it) => it.person) } })
|
||||
// for (const record of users) {
|
||||
// if (record.error !== undefined) {
|
||||
// // Skip accounts with error
|
||||
// continue
|
||||
// }
|
||||
// const account = accounts.find((it) => it.email === `github:${record._id}`)
|
||||
// const userAuth = userAuths.find((it) => it.login === record._id)
|
||||
// const person = persons.find((it) => account?.person)
|
||||
// if (account === undefined || userAuth === undefined || person === undefined) {
|
||||
// continue
|
||||
// }
|
||||
// const accountRef = record.accounts[this.workspace.name]
|
||||
// try {
|
||||
// await this.platform.checkRefreshToken(record, true)
|
||||
|
||||
const ops = new TxOperations(this.client, accountRef)
|
||||
await syncUser(ctx, record, userAuth, ops, accountRef)
|
||||
} catch (err: any) {
|
||||
try {
|
||||
await this.platform.revokeUserAuth(record)
|
||||
} catch (err: any) {
|
||||
ctx.error(`Failed to revoke user ${record._id}`, err)
|
||||
}
|
||||
if (err.response?.data?.message !== 'Bad credentials') {
|
||||
ctx.error(`Failed to sync user ${record._id}`, err)
|
||||
Analytics.handleError(err)
|
||||
}
|
||||
if (userAuth !== undefined) {
|
||||
await this._client.update<GithubAuthentication>(
|
||||
userAuth,
|
||||
{
|
||||
error: errorToObj(err)
|
||||
},
|
||||
undefined,
|
||||
Date.now(),
|
||||
accountRef
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// const ops = new TxOperations(this.client, accountRef)
|
||||
// await syncUser(ctx, record, userAuth, ops, accountRef)
|
||||
// } catch (err: any) {
|
||||
// try {
|
||||
// await this.platform.revokeUserAuth(record)
|
||||
// } catch (err: any) {
|
||||
// ctx.error(`Failed to revoke user ${record._id}`, err)
|
||||
// }
|
||||
// if (err.response?.data?.message !== 'Bad credentials') {
|
||||
// ctx.error(`Failed to sync user ${record._id}`, err)
|
||||
// Analytics.handleError(err)
|
||||
// }
|
||||
// if (userAuth !== undefined) {
|
||||
// await this._client.update<GithubAuthentication>(
|
||||
// userAuth,
|
||||
// {
|
||||
// error: errorToObj(err)
|
||||
// },
|
||||
// undefined,
|
||||
// Date.now(),
|
||||
// accountRef
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
async getOctokit (account: Ref<PersonAccount>): Promise<Octokit | undefined> {
|
||||
let record = await this.platform.getAccountByRef(this.workspace.name, account)
|
||||
async getOctokit (account: PersonId): Promise<Octokit | undefined> {
|
||||
// TODO: FIXME
|
||||
throw new Error('Not implemented')
|
||||
// let record = await this.platform.getAccountByRef(this.workspace.name, account)
|
||||
|
||||
// const accountRef = this.accounts.find((it) => it._id === account)
|
||||
const [accountRef] = this.client.getModel().findAllSync(contact.class.PersonAccount, { _id: account })
|
||||
if (record === undefined) {
|
||||
if (accountRef !== undefined) {
|
||||
const accounts = this._client.getModel().getAccountByPersonId(accountRef.person)
|
||||
for (const aa of accounts) {
|
||||
record = await this.platform.getAccountByRef(this.workspace.name, aa._id)
|
||||
if (record !== undefined) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check and refresh token if required.
|
||||
if (record !== undefined) {
|
||||
this.ctx.info('get octokit', { account, recordId: record._id, workspace: this.workspace.name })
|
||||
await this.platform.checkRefreshToken(record)
|
||||
return new Octokit({
|
||||
auth: record.token,
|
||||
client_id: config.ClientID,
|
||||
client_secret: config.ClientSecret
|
||||
})
|
||||
}
|
||||
// const [accountRef] = this.client.getModel().findAllSync(contact.class.PersonAccount, { _id: account })
|
||||
// if (record === undefined) {
|
||||
// if (accountRef !== undefined) {
|
||||
// const accounts = this._client.getModel().getAccountByPersonId(accountRef.person)
|
||||
// for (const aa of accounts) {
|
||||
// record = await this.platform.getAccountByRef(this.workspace.name, aa._id)
|
||||
// if (record !== undefined) {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // Check and refresh token if required.
|
||||
// if (record !== undefined) {
|
||||
// this.ctx.info('get octokit', { account, recordId: record._id, workspace: this.workspace.name })
|
||||
// await this.platform.checkRefreshToken(record)
|
||||
// return new Octokit({
|
||||
// auth: record.token,
|
||||
// client_id: config.ClientID,
|
||||
// client_secret: config.ClientSecret
|
||||
// })
|
||||
// }
|
||||
|
||||
// We need to inform user, he need to authorize this account with github.
|
||||
if (accountRef !== undefined && !this.authRequestSend.has(accountRef._id)) {
|
||||
this.authRequestSend.add(accountRef._id)
|
||||
const person = await this.liveQuery.findOne(contact.class.Person, { _id: accountRef.person })
|
||||
if (person !== undefined) {
|
||||
const personSpace = await this.liveQuery.findOne(contact.class.PersonSpace, { person: person._id })
|
||||
if (personSpace !== undefined) {
|
||||
// We need to remove if user has authentication in workspace but doesn't have a record.
|
||||
// // We need to inform user, he need to authorize this account with github.
|
||||
// if (accountRef !== undefined && !this.authRequestSend.has(accountRef._id)) {
|
||||
// this.authRequestSend.add(accountRef._id)
|
||||
// const person = await this.liveQuery.findOne(contact.class.Person, { _id: accountRef.person })
|
||||
// if (person !== undefined) {
|
||||
// const personSpace = await this.liveQuery.findOne(contact.class.PersonSpace, { person: person._id })
|
||||
// if (personSpace !== undefined) {
|
||||
// // We need to remove if user has authentication in workspace but doesn't have a record.
|
||||
|
||||
const accounts = this._client.getModel().getAccountByPersonId(accountRef.person)
|
||||
const authentications = await this.liveQuery.findAll(github.class.GithubAuthentication, {
|
||||
createdBy: { $in: accounts.map((it) => it._id) }
|
||||
})
|
||||
for (const auth of authentications) {
|
||||
await this._client.remove(auth)
|
||||
}
|
||||
// const accounts = this._client.getModel().getAccountByPersonId(accountRef.person)
|
||||
// const authentications = await this.liveQuery.findAll(github.class.GithubAuthentication, {
|
||||
// createdBy: { $in: accounts.map((it) => it._id) }
|
||||
// })
|
||||
// for (const auth of authentications) {
|
||||
// await this._client.remove(auth)
|
||||
// }
|
||||
|
||||
await createNotification(this._client, person, {
|
||||
user: account,
|
||||
space: personSpace._id,
|
||||
message: github.string.AuthenticatedWithGithubRequired,
|
||||
props: {}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
this.ctx.info('get octokit: return bot', { account, workspace: this.workspace.name })
|
||||
// await createNotification(this._client, person, {
|
||||
// user: account,
|
||||
// space: personSpace._id,
|
||||
// message: github.string.AuthenticatedWithGithubRequired,
|
||||
// props: {}
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// this.ctx.info('get octokit: return bot', { account, workspace: this.workspace.name })
|
||||
}
|
||||
|
||||
async isPlatformUser (account: Ref<PersonAccount>): Promise<boolean> {
|
||||
let record = await this.platform.getAccountByRef(this.workspace.name, account)
|
||||
const accountRef = await this.liveQuery.findOne(contact.class.PersonAccount, { _id: account })
|
||||
if (record === undefined) {
|
||||
if (accountRef !== undefined) {
|
||||
const accounts = this._client.getModel().getAccountByPersonId(accountRef.person)
|
||||
for (const aa of accounts) {
|
||||
record = await this.platform.getAccountByRef(this.workspace.name, aa._id)
|
||||
if (record !== undefined) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check and refresh token if required.
|
||||
return record !== undefined && accountRef !== undefined
|
||||
async isPlatformUser (account: PersonId): Promise<boolean> {
|
||||
// TODO: FIXME
|
||||
throw new Error('Not implemented')
|
||||
// let record = await this.platform.getAccountByRef(this.workspace.name, account)
|
||||
// const accountRef = await this.liveQuery.findOne(contact.class.PersonAccount, { _id: account })
|
||||
// if (record === undefined) {
|
||||
// if (accountRef !== undefined) {
|
||||
// const accounts = this._client.getModel().getAccountByPersonId(accountRef.person)
|
||||
// for (const aa of accounts) {
|
||||
// record = await this.platform.getAccountByRef(this.workspace.name, aa._id)
|
||||
// if (record !== undefined) {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // Check and refresh token if required.
|
||||
// return record !== undefined && accountRef !== undefined
|
||||
}
|
||||
|
||||
async uploadFile (patch: string, file?: string, contentType?: string): Promise<Blob | undefined> {
|
||||
const id: string = file ?? generateId()
|
||||
await this.storageAdapter.put(this.ctx, this.workspace, id, patch, contentType ?? 'text/x-patch')
|
||||
return await this.storageAdapter.stat(this.ctx, this.workspace, id)
|
||||
const dataId = this.workspace.dataId ?? (this.workspace.uuid as unknown as WorkspaceDataId)
|
||||
await this.storageAdapter.put(this.ctx, dataId, id, patch, contentType ?? 'text/x-patch')
|
||||
return await this.storageAdapter.stat(this.ctx, dataId, id)
|
||||
}
|
||||
|
||||
integrationRepositories: WithLookup<GithubIntegrationRepository>[] = []
|
||||
@@ -729,7 +753,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
this.updateRequests = 1
|
||||
this.syncPromise = this.syncAndWait()
|
||||
|
||||
const userRecords = await this.platform.getUsers(this.workspace.name)
|
||||
const userRecords = await this.platform.getUsers(this.workspace.uuid)
|
||||
try {
|
||||
await this.syncUserData(this.ctx, userRecords)
|
||||
} catch (err: any) {
|
||||
@@ -850,57 +874,59 @@ export class GithubWorker implements IntegrationManager {
|
||||
}
|
||||
|
||||
private async queryAccounts (): Promise<void> {
|
||||
const updateAccounts = async (accounts: PersonAccount[]): Promise<void> => {
|
||||
const persons = await this.liveQuery.findAll(contact.class.Person, {
|
||||
_id: { $in: accounts.map((it) => it.person) }
|
||||
})
|
||||
const h = this.client.getHierarchy()
|
||||
for (const a of accounts) {
|
||||
if (a.email.startsWith('github:')) {
|
||||
const login = a.email.substring(7)
|
||||
const person = persons.find((it) => it._id === a.person)
|
||||
if (person !== undefined) {
|
||||
// #1 check if person has GithubUser mixin.
|
||||
if (!h.hasMixin(person, github.mixin.GithubUser)) {
|
||||
await this._client.createMixin(person._id, person._class, person.space, github.mixin.GithubUser, {
|
||||
url: `https://github.com/${login}`
|
||||
})
|
||||
} else {
|
||||
const ghu = h.as(person, github.mixin.GithubUser)
|
||||
if (ghu.url !== `https://github.com/${login}`) {
|
||||
await this._client.updateMixin(person._id, person._class, person.space, github.mixin.GithubUser, {
|
||||
url: `https://github.com/${login}`
|
||||
})
|
||||
}
|
||||
}
|
||||
// #2 check if person has contact github and if not add it.
|
||||
const channel = await this._client.findOne(contact.class.Channel, {
|
||||
provider: contact.channelProvider.GitHub,
|
||||
value: login,
|
||||
attachedTo: person._id
|
||||
})
|
||||
if (channel === undefined) {
|
||||
await this._client.addCollection(
|
||||
contact.class.Channel,
|
||||
person.space,
|
||||
person._id,
|
||||
contact.class.Person,
|
||||
'channels',
|
||||
{
|
||||
provider: contact.channelProvider.GitHub,
|
||||
value: login
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
this.liveQuery.query(contact.class.PersonAccount, {}, (res) => {
|
||||
void updateAccounts(res).then(resolve).catch(reject)
|
||||
})
|
||||
})
|
||||
// TODO: FIXME
|
||||
throw new Error('Not implemented')
|
||||
// const updateAccounts = async (accounts: PersonAccount[]): Promise<void> => {
|
||||
// const persons = await this.liveQuery.findAll(contact.class.Person, {
|
||||
// _id: { $in: accounts.map((it) => it.person) }
|
||||
// })
|
||||
// const h = this.client.getHierarchy()
|
||||
// for (const a of accounts) {
|
||||
// if (a.email.startsWith('github:')) {
|
||||
// const login = a.email.substring(7)
|
||||
// const person = persons.find((it) => it._id === a.person)
|
||||
// if (person !== undefined) {
|
||||
// // #1 check if person has GithubUser mixin.
|
||||
// if (!h.hasMixin(person, github.mixin.GithubUser)) {
|
||||
// await this._client.createMixin(person._id, person._class, person.space, github.mixin.GithubUser, {
|
||||
// url: `https://github.com/${login}`
|
||||
// })
|
||||
// } else {
|
||||
// const ghu = h.as(person, github.mixin.GithubUser)
|
||||
// if (ghu.url !== `https://github.com/${login}`) {
|
||||
// await this._client.updateMixin(person._id, person._class, person.space, github.mixin.GithubUser, {
|
||||
// url: `https://github.com/${login}`
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// // #2 check if person has contact github and if not add it.
|
||||
// const channel = await this._client.findOne(contact.class.Channel, {
|
||||
// provider: contact.channelProvider.GitHub,
|
||||
// value: login,
|
||||
// attachedTo: person._id
|
||||
// })
|
||||
// if (channel === undefined) {
|
||||
// await this._client.addCollection(
|
||||
// contact.class.Channel,
|
||||
// person.space,
|
||||
// person._id,
|
||||
// contact.class.Person,
|
||||
// 'channels',
|
||||
// {
|
||||
// provider: contact.channelProvider.GitHub,
|
||||
// value: login
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// await new Promise<void>((resolve, reject) => {
|
||||
// this.liveQuery.query(contact.class.PersonAccount, {}, (res) => {
|
||||
// void updateAccounts(res).then(resolve).catch(reject)
|
||||
// })
|
||||
// })
|
||||
}
|
||||
|
||||
async performExternalSync (
|
||||
@@ -960,7 +986,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
field,
|
||||
version,
|
||||
docs: docs.length,
|
||||
workspace: this.workspace.name
|
||||
workspace: this.workspace.uuid
|
||||
})
|
||||
|
||||
const byClass = this.groupByClass(docs)
|
||||
@@ -1128,7 +1154,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
|
||||
if (!hadExternalChanges && !hadSyncChanges && !hadDerivedChanges) {
|
||||
if (this.previousWait !== 0) {
|
||||
this.ctx.info('Wait for changes:', { previousWait: this.previousWait, workspace: this.workspace.name })
|
||||
this.ctx.info('Wait for changes:', { previousWait: this.previousWait, workspace: this.workspace.uuid })
|
||||
this.previousWait = 0
|
||||
}
|
||||
// Wait until some sync documents will be modified, updated.
|
||||
@@ -1170,7 +1196,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
|
||||
if (docs.length > 0) {
|
||||
this.previousWait += docs.length
|
||||
this.ctx.info('Syncing', { docs: docs.length, workspace: this.workspace.name })
|
||||
this.ctx.info('Syncing', { docs: docs.length, workspace: this.workspace.uuid })
|
||||
|
||||
const bySpace = groupByArray(docs, (it) => it.space)
|
||||
for (const [k, v] of bySpace.entries()) {
|
||||
@@ -1232,7 +1258,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
}
|
||||
|
||||
async checkMapping (): Promise<void> {
|
||||
for (const intgr of this.platform.integrations.filter((it) => it.workspace === this.workspace.name)) {
|
||||
for (const intgr of this.platform.integrations.filter((it) => it.workspace === this.workspace.uuid)) {
|
||||
const integration = await this._client.findOne(github.class.GithubIntegration, {
|
||||
installationId: intgr.installationId
|
||||
})
|
||||
@@ -1298,7 +1324,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
const existing = externalDocs.find((it) => it._id === info._id)
|
||||
const mapper = this.mappers.find((it) => it._class.includes(info.objectClass))?.mapper
|
||||
if (mapper === undefined) {
|
||||
this.ctx.info('No mapper for class', { objectClass: info.objectClass, workspace: this.workspace.name })
|
||||
this.ctx.info('No mapper for class', { objectClass: info.objectClass, workspace: this.workspace.uuid })
|
||||
await derivedClient.update<DocSyncInfo>(info, {
|
||||
needSync: githubSyncVersion
|
||||
})
|
||||
@@ -1371,7 +1397,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
'sync doc',
|
||||
{},
|
||||
(ctx) => mapper.sync(existing, info, parent, derivedClient),
|
||||
{ url: info.url.toLowerCase(), workspace: this.workspace.name }
|
||||
{ url: info.url.toLowerCase(), workspace: this.workspace.uuid }
|
||||
)
|
||||
if (docUpdate !== undefined) {
|
||||
await derivedClient.update(info, docUpdate)
|
||||
@@ -1398,7 +1424,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
this.ctx.info('Trigger check pending:', {
|
||||
requests: this.triggerRequests,
|
||||
updates: this.updateRequests,
|
||||
workspace: this.workspace.name
|
||||
workspace: this.workspace.uuid
|
||||
})
|
||||
this.triggerRequests = 0
|
||||
return
|
||||
@@ -1413,7 +1439,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
triggerTimeout = setTimeout(() => {
|
||||
triggerTimeout = undefined
|
||||
if (was0) {
|
||||
this.ctx.info('Sync triggered', { request: this.triggerRequests, workspace: this.workspace.name })
|
||||
this.ctx.info('Sync triggered', { request: this.triggerRequests, workspace: this.workspace.uuid })
|
||||
}
|
||||
resolve()
|
||||
}, 50) // Small timeout to aggregate few bulk changes.
|
||||
@@ -1427,7 +1453,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
updateTimeout = setTimeout(() => {
|
||||
updateTimeout = undefined
|
||||
if (was0) {
|
||||
this.ctx.info('Sync update triggered', { requests: this.updateRequests, workspace: this.workspace.name })
|
||||
this.ctx.info('Sync update triggered', { requests: this.updateRequests, workspace: this.workspace.uuid })
|
||||
}
|
||||
resolve()
|
||||
}, 50) // Small timeout to aggregate few bulk changes.
|
||||
@@ -1452,7 +1478,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
}
|
||||
await this.ctx.withLog(
|
||||
'external sync',
|
||||
{ installation: integration.installationName, workspace: this.workspace.name },
|
||||
{ installation: integration.installationName, workspace: this.workspace.uuid },
|
||||
async () => {
|
||||
const enabled = integration.enabled && integration.octokit !== undefined
|
||||
|
||||
@@ -1515,7 +1541,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
}
|
||||
await this.ctx.withLog(
|
||||
'external sync',
|
||||
{ _class: _class.join(', '), workspace: this.workspace.name },
|
||||
{ _class: _class.join(', '), workspace: this.workspace.uuid },
|
||||
async () => {
|
||||
await mapper.externalFullSync(integration, derivedClient, _projects, _repositories)
|
||||
}
|
||||
@@ -1565,18 +1591,18 @@ export class GithubWorker implements IntegrationManager {
|
||||
platformWorker: PlatformWorker,
|
||||
ctx: MeasureContext,
|
||||
installations: Map<number, InstallationRecord>,
|
||||
workspace: WorkspaceIdWithUrl,
|
||||
workspace: WorkspaceIds,
|
||||
branding: Branding | null,
|
||||
app: App,
|
||||
storageAdapter: StorageAdapter,
|
||||
reconnect: (workspaceId: string, event: ClientConnectEvent) => void
|
||||
): Promise<GithubWorker | undefined> {
|
||||
ctx.info('Connecting to', { workspace: workspace.workspaceUrl, workspaceId: workspace.workspaceName })
|
||||
ctx.info('Connecting to', { workspace })
|
||||
let client: Client | undefined
|
||||
let endpoint: string | undefined
|
||||
try {
|
||||
;({ client, endpoint } = await createPlatformClient(workspace.name, 30000, async (event: ClientConnectEvent) => {
|
||||
reconnect(workspace.name, event)
|
||||
;({ client, endpoint } = await createPlatformClient(workspace.uuid, 30000, async (event: ClientConnectEvent) => {
|
||||
reconnect(workspace.uuid, event)
|
||||
}))
|
||||
|
||||
await GithubWorker.checkIntegrations(client, installations)
|
||||
@@ -1592,14 +1618,8 @@ export class GithubWorker implements IntegrationManager {
|
||||
workspace,
|
||||
branding
|
||||
)
|
||||
ctx.info('Init worker', { workspace: workspace.workspaceUrl, workspaceId: workspace.workspaceName })
|
||||
void worker.init().catch((err) => {
|
||||
ctx.error('Failed to init worker', {
|
||||
workspace: workspace.workspaceUrl,
|
||||
workspaceId: workspace.workspaceName,
|
||||
error: err
|
||||
})
|
||||
})
|
||||
ctx.info('Init worker', { workspace: workspace.url, workspaceId: workspace.uuid })
|
||||
void worker.init()
|
||||
return worker
|
||||
} catch (err: any) {
|
||||
ctx.error('timeout during to connect', { workspace, error: err })
|
||||
@@ -1624,7 +1644,7 @@ export async function syncUser (
|
||||
record: GithubUserRecord,
|
||||
userAuth: WithLookup<GithubAuthentication>,
|
||||
client: TxOperations,
|
||||
account: Ref<Account>
|
||||
account: PersonId
|
||||
): Promise<void> {
|
||||
const okit = new Octokit({
|
||||
auth: record.token,
|
||||
|
||||
Reference in New Issue
Block a user