mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-12 19:30:52 +00:00
UBERF-6194: CLI for rename account (#5067)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
5716c053fd
commit
9f929f0290
@ -82,4 +82,4 @@ function prepareTools (): {
|
||||
|
||||
console.log(`tools git_version: ${process.env.GIT_REVISION ?? ''} model_version: ${process.env.MODEL_VERSION ?? ''}`)
|
||||
|
||||
devTool(prepareTools, '')
|
||||
devTool(prepareTools, process.env.PRODUCT_ID ?? '')
|
||||
|
@ -79,6 +79,7 @@ import { checkOrphanWorkspaces } from './cleanOrphan'
|
||||
import { changeConfiguration } from './configuration'
|
||||
import { fixMixinForeignAttributes, showMixinForeignAttributes } from './mixin'
|
||||
import { openAIConfig } from './openai'
|
||||
import { fixAccountEmails, renameAccount } from './renameAccount'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -162,6 +163,28 @@ export function devTool (
|
||||
})
|
||||
})
|
||||
|
||||
program
|
||||
.command('reset-email <email> <newEmail>')
|
||||
.description('rename account in accounts and all workspaces')
|
||||
.action(async (email: string, newEmail: string, cmd) => {
|
||||
const { mongodbUri } = prepareTools()
|
||||
await withDatabase(mongodbUri, async (db) => {
|
||||
console.log(`update account ${email} to ${newEmail}`)
|
||||
await renameAccount(toolCtx, db, productId, transactorUrl, email, newEmail)
|
||||
})
|
||||
})
|
||||
|
||||
program
|
||||
.command('fix-email <email> <newEmail>')
|
||||
.description('fix email in all workspaces to be proper one')
|
||||
.action(async (email: string, newEmail: string, cmd) => {
|
||||
const { mongodbUri } = prepareTools()
|
||||
await withDatabase(mongodbUri, async (db) => {
|
||||
console.log(`update account ${email} to ${newEmail}`)
|
||||
await fixAccountEmails(toolCtx, db, productId, transactorUrl, email, newEmail)
|
||||
})
|
||||
})
|
||||
|
||||
program
|
||||
.command('assign-workspace <email> <workspace>')
|
||||
.description('assign workspace')
|
||||
@ -433,8 +456,16 @@ export function devTool (
|
||||
.action(async () => {
|
||||
const { mongodbUri } = prepareTools()
|
||||
await withDatabase(mongodbUri, async (db) => {
|
||||
const accountsJSON = JSON.stringify(await listAccounts(db), null, 2)
|
||||
console.info(accountsJSON)
|
||||
const workspaces = await listWorkspacesPure(db, productId)
|
||||
const accounts = await listAccounts(db)
|
||||
for (const a of accounts) {
|
||||
const wss = a.workspaces.map((it) => it.toString())
|
||||
console.info(
|
||||
a.email,
|
||||
a.confirmed,
|
||||
workspaces.filter((it) => wss.includes(it._id.toString())).map((it) => it.workspaceUrl ?? it.workspace)
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
91
dev/tool/src/renameAccount.ts
Normal file
91
dev/tool/src/renameAccount.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { type Account, changeEmail, getAccount, listWorkspacesPure, type Workspace } from '@hcengineering/account'
|
||||
import core, { type MeasureContext, TxOperations } from '@hcengineering/core'
|
||||
import contact from '@hcengineering/model-contact'
|
||||
import { connect } from '@hcengineering/server-tool'
|
||||
import { type Db } from 'mongodb'
|
||||
|
||||
export async function renameAccount (
|
||||
ctx: MeasureContext,
|
||||
db: Db,
|
||||
productId: string,
|
||||
transactorUrl: string,
|
||||
oldEmail: string,
|
||||
newEmail: string
|
||||
): Promise<void> {
|
||||
const account = await getAccount(db, oldEmail)
|
||||
if (account == null) {
|
||||
throw new Error("Account does'n exists")
|
||||
}
|
||||
|
||||
const newAccount = await getAccount(db, newEmail)
|
||||
if (newAccount != null) {
|
||||
throw new Error('New Account email already exists:' + newAccount?.email + ' ' + newAccount?._id?.toString())
|
||||
}
|
||||
|
||||
await changeEmail(ctx, db, account, newEmail)
|
||||
|
||||
await fixWorkspaceEmails(account, db, productId, transactorUrl, oldEmail, newEmail)
|
||||
}
|
||||
|
||||
export async function fixAccountEmails (
|
||||
ctx: MeasureContext,
|
||||
db: Db,
|
||||
productId: string,
|
||||
transactorUrl: string,
|
||||
oldEmail: string,
|
||||
newEmail: string
|
||||
): Promise<void> {
|
||||
const account = await getAccount(db, newEmail)
|
||||
if (account == null) {
|
||||
throw new Error("Account does'n exists")
|
||||
}
|
||||
|
||||
await fixWorkspaceEmails(account, db, productId, transactorUrl, oldEmail, newEmail)
|
||||
}
|
||||
async function fixWorkspaceEmails (
|
||||
account: Account,
|
||||
db: Db,
|
||||
productId: string,
|
||||
transactorUrl: string,
|
||||
oldEmail: string,
|
||||
newEmail: string
|
||||
): Promise<void> {
|
||||
const accountWorkspaces = account.workspaces.map((it) => it.toString())
|
||||
// We need to update all workspaces
|
||||
const workspaces = await listWorkspacesPure(db, productId)
|
||||
for (const ws of workspaces) {
|
||||
if (!accountWorkspaces.includes(ws._id.toString())) {
|
||||
continue
|
||||
}
|
||||
console.log('checking workspace', ws.workspaceName, ws.workspace)
|
||||
|
||||
// Let's connect and update account information.
|
||||
await fixEmailInWorkspace(transactorUrl, ws, oldEmail, newEmail)
|
||||
}
|
||||
}
|
||||
|
||||
async function fixEmailInWorkspace (
|
||||
transactorUrl: string,
|
||||
ws: Workspace,
|
||||
oldEmail: string,
|
||||
newEmail: string
|
||||
): Promise<void> {
|
||||
const connection = await connect(transactorUrl, { name: ws.workspace, productId: ws.productId }, undefined, {
|
||||
mode: 'backup',
|
||||
model: 'upgrade', // Required for force all clients reload after operation will be complete.
|
||||
admin: 'true'
|
||||
})
|
||||
try {
|
||||
const personAccount = await connection.findOne(contact.class.PersonAccount, { email: oldEmail })
|
||||
|
||||
if (personAccount !== undefined) {
|
||||
console.log('update account in ', ws.workspace)
|
||||
const ops = new TxOperations(connection, core.account.ConfigUser)
|
||||
await ops.update(personAccount, { email: newEmail })
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
} finally {
|
||||
await connection.close()
|
||||
}
|
||||
}
|
@ -13,29 +13,36 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Channel } from '@hcengineering/contact'
|
||||
import { Data } from '@hcengineering/core'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { copyTextToClipboard } from '@hcengineering/presentation'
|
||||
import {
|
||||
PopupOptions,
|
||||
themeStore,
|
||||
Button,
|
||||
createFocusManager,
|
||||
FocusHandler,
|
||||
IconArrowRight,
|
||||
IconBlueCheck,
|
||||
IconClose,
|
||||
registerFocus
|
||||
IconMoreV,
|
||||
PopupOptions,
|
||||
createFocusManager,
|
||||
getEventPopupPositionElement,
|
||||
registerFocus,
|
||||
showPopup,
|
||||
themeStore
|
||||
} from '@hcengineering/ui'
|
||||
import { ContextMenu } from '@hcengineering/view-resources'
|
||||
import view from '@hcengineering/view'
|
||||
import { afterUpdate, createEventDispatcher, onMount } from 'svelte'
|
||||
import plugin from '../plugin'
|
||||
import IconCopy from './icons/Copy.svelte'
|
||||
|
||||
export let value: string = ''
|
||||
export let placeholder: IntlString
|
||||
export let editable: boolean | undefined = undefined
|
||||
export let openable: boolean = false
|
||||
export let popupOptions: PopupOptions
|
||||
export let channel: Data<Channel> | Channel
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let input: HTMLInputElement
|
||||
@ -144,6 +151,26 @@
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{#if '_id' in channel}
|
||||
<Button
|
||||
kind={'ghost'}
|
||||
size={'small'}
|
||||
icon={IconMoreV}
|
||||
on:click={(evt) => {
|
||||
showPopup(
|
||||
ContextMenu,
|
||||
{
|
||||
object: channel,
|
||||
includedActions: [view.action.Delete]
|
||||
},
|
||||
getEventPopupPositionElement(evt),
|
||||
() => {
|
||||
dispatch('close')
|
||||
}
|
||||
)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex-row-center gap-2">
|
||||
|
@ -29,11 +29,13 @@
|
||||
Menu,
|
||||
closeTooltip,
|
||||
eventToHTMLElement,
|
||||
getEventPopupPositionElement,
|
||||
getFocusManager,
|
||||
getPopupPositionElement,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import view, { Action as ViewAction } from '@hcengineering/view'
|
||||
import { invokeAction } from '@hcengineering/view-resources'
|
||||
import { ContextMenu, invokeAction } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, tick } from 'svelte'
|
||||
import { readable, Readable, Writable, writable } from 'svelte/store'
|
||||
import { channelProviders } from '../utils'
|
||||
@ -222,7 +224,8 @@
|
||||
value: item.value,
|
||||
placeholder: item.placeholder,
|
||||
editable,
|
||||
openable: item.presenter ?? item.action ?? false
|
||||
openable: item.presenter ?? item.action ?? false,
|
||||
channel: item.channel
|
||||
},
|
||||
el,
|
||||
(result) => {
|
||||
|
@ -24,6 +24,7 @@
|
||||
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let actions: Action[] = []
|
||||
export let excludedActions: string[] = []
|
||||
export let includedActions: string[] = []
|
||||
export let mode: ViewContextType | undefined = undefined
|
||||
let resActions = actions
|
||||
|
||||
@ -41,11 +42,14 @@
|
||||
remove: 7
|
||||
}
|
||||
|
||||
getActions(client, object, baseMenuClass, mode).then((result) => {
|
||||
void getActions(client, object, baseMenuClass, mode).then((result) => {
|
||||
const filtered = result.filter((a) => {
|
||||
if (excludedActions.includes(a._id)) {
|
||||
return false
|
||||
}
|
||||
if (includedActions.length > 0 && !includedActions.includes(a._id)) {
|
||||
return false
|
||||
}
|
||||
if (a.override && a.override.filter((o) => excludedActions.includes(o)).length > 0) {
|
||||
return false
|
||||
}
|
||||
|
@ -668,7 +668,6 @@ export async function listWorkspacesPure (db: Db, productId: string): Promise<Wo
|
||||
(it) => ({ ...it, productId })
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -1372,6 +1371,14 @@ export async function changePassword (
|
||||
await ctx.info('change-password success', { email })
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function changeEmail (ctx: MeasureContext, db: Db, account: Account, newEmail: string): Promise<void> {
|
||||
await db.collection<Account>(ACCOUNT_COLLECTION).updateOne({ _id: account._id }, { $set: { email: newEmail } })
|
||||
await ctx.info('change-email success', { email: newEmail })
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user