mirror of
https://github.com/hcengineering/platform.git
synced 2025-03-19 13:18:56 +00:00
Merge remote-tracking branch 'origin/develop' into staging
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
commit
6e57d4cb38
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -129,7 +129,7 @@
|
|||||||
"MINIO_ACCESS_KEY": "minioadmin",
|
"MINIO_ACCESS_KEY": "minioadmin",
|
||||||
"MINIO_SECRET_KEY": "minioadmin",
|
"MINIO_SECRET_KEY": "minioadmin",
|
||||||
"MINIO_ENDPOINT": "localhost",
|
"MINIO_ENDPOINT": "localhost",
|
||||||
"MODEL_VERSION": "v0.6.286"
|
"MODEL_VERSION": "v0.6.287"
|
||||||
// "INIT_SCRIPT_URL": "https://raw.githubusercontent.com/hcengineering/init/main/script.yaml",
|
// "INIT_SCRIPT_URL": "https://raw.githubusercontent.com/hcengineering/init/main/script.yaml",
|
||||||
// "INIT_WORKSPACE": "onboarding",
|
// "INIT_WORKSPACE": "onboarding",
|
||||||
},
|
},
|
||||||
|
@ -1503,7 +1503,7 @@ export function devTool (
|
|||||||
.option('-w, --workspace <workspace>', 'Selected workspace only', '')
|
.option('-w, --workspace <workspace>', 'Selected workspace only', '')
|
||||||
.option('-c, --concurrency <concurrency>', 'Number of documents being processed concurrently', '10')
|
.option('-c, --concurrency <concurrency>', 'Number of documents being processed concurrently', '10')
|
||||||
.action(async (cmd: { workspace: string, concurrency: string }) => {
|
.action(async (cmd: { workspace: string, concurrency: string }) => {
|
||||||
const { mongodbUri, dbUrl } = prepareTools()
|
const { mongodbUri, dbUrl, txes } = prepareTools()
|
||||||
await withDatabase(mongodbUri, async (db, client) => {
|
await withDatabase(mongodbUri, async (db, client) => {
|
||||||
await withStorage(mongodbUri, async (adapter) => {
|
await withStorage(mongodbUri, async (adapter) => {
|
||||||
const workspaces = await listWorkspacesPure(db)
|
const workspaces = await listWorkspacesPure(db)
|
||||||
@ -1521,7 +1521,7 @@ export function devTool (
|
|||||||
workspaceUrl: workspace.workspaceUrl ?? ''
|
workspaceUrl: workspace.workspaceUrl ?? ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const { pipeline } = await getServerPipeline(toolCtx, mongodbUri, dbUrl, wsUrl)
|
const { pipeline } = await getServerPipeline(toolCtx, txes, mongodbUri, dbUrl, wsUrl)
|
||||||
|
|
||||||
await migrateMarkup(toolCtx, adapter, wsId, client, pipeline, parseInt(cmd.concurrency))
|
await migrateMarkup(toolCtx, adapter, wsId, client, pipeline, parseInt(cmd.concurrency))
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import core, {
|
|||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { getMongoClient, getWorkspaceDB } from '@hcengineering/mongo'
|
import { getMongoClient, getWorkspaceDB } from '@hcengineering/mongo'
|
||||||
import { type Pipeline, type StorageAdapter } from '@hcengineering/server-core'
|
import { type Pipeline, type StorageAdapter } from '@hcengineering/server-core'
|
||||||
import { connect, fetchModel } from '@hcengineering/server-tool'
|
import { connect } from '@hcengineering/server-tool'
|
||||||
import { jsonToText, markupToYDoc } from '@hcengineering/text'
|
import { jsonToText, markupToYDoc } from '@hcengineering/text'
|
||||||
import { type Db, type FindCursor, type MongoClient } from 'mongodb'
|
import { type Db, type FindCursor, type MongoClient } from 'mongodb'
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ export async function migrateMarkup (
|
|||||||
pipeline: Pipeline,
|
pipeline: Pipeline,
|
||||||
concurrency: number
|
concurrency: number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { hierarchy } = await fetchModel(ctx, pipeline)
|
const hierarchy = pipeline.context.hierarchy
|
||||||
|
|
||||||
const workspaceDb = client.db(workspaceId.name)
|
const workspaceDb = client.db(workspaceId.name)
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ import {
|
|||||||
type Data,
|
type Data,
|
||||||
type Doc,
|
type Doc,
|
||||||
type DocumentQuery,
|
type DocumentQuery,
|
||||||
type Domain,
|
|
||||||
type IndexingConfiguration,
|
type IndexingConfiguration,
|
||||||
type Markup,
|
type Markup,
|
||||||
type Ref,
|
type Ref,
|
||||||
@ -56,6 +55,9 @@ import view, { createAction, template } from '@hcengineering/model-view'
|
|||||||
import workbench from '@hcengineering/model-workbench'
|
import workbench from '@hcengineering/model-workbench'
|
||||||
import {
|
import {
|
||||||
notificationId,
|
notificationId,
|
||||||
|
DOMAIN_USER_NOTIFY,
|
||||||
|
DOMAIN_NOTIFICATION,
|
||||||
|
DOMAIN_DOC_NOTIFY,
|
||||||
type ActivityInboxNotification,
|
type ActivityInboxNotification,
|
||||||
type ActivityNotificationViewlet,
|
type ActivityNotificationViewlet,
|
||||||
type BaseNotificationType,
|
type BaseNotificationType,
|
||||||
@ -86,16 +88,10 @@ import { type AnyComponent, type Location } from '@hcengineering/ui/src/types'
|
|||||||
|
|
||||||
import notification from './plugin'
|
import notification from './plugin'
|
||||||
|
|
||||||
export { notificationId } from '@hcengineering/notification'
|
export { notificationId, DOMAIN_USER_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_DOC_NOTIFY } from '@hcengineering/notification'
|
||||||
export { notificationOperation } from './migration'
|
export { notificationOperation } from './migration'
|
||||||
export { notification as default }
|
export { notification as default }
|
||||||
|
|
||||||
export const DOMAIN_NOTIFICATION = 'notification' as Domain
|
|
||||||
|
|
||||||
export const DOMAIN_DOC_NOTIFY = 'notification-dnc' as Domain
|
|
||||||
|
|
||||||
export const DOMAIN_USER_NOTIFY = 'notification-user' as Domain
|
|
||||||
|
|
||||||
@Model(notification.class.BrowserNotification, core.class.Doc, DOMAIN_USER_NOTIFY)
|
@Model(notification.class.BrowserNotification, core.class.Doc, DOMAIN_USER_NOTIFY)
|
||||||
export class TBrowserNotification extends TDoc implements BrowserNotification {
|
export class TBrowserNotification extends TDoc implements BrowserNotification {
|
||||||
senderId?: Ref<Account> | undefined
|
senderId?: Ref<Account> | undefined
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
export let lazy = false
|
export let lazy = false
|
||||||
export let minHeight: string | null = null
|
export let minHeight: string | null = null
|
||||||
export let highlightIndex: number | undefined = undefined
|
export let highlightIndex: number | undefined = undefined
|
||||||
const getKey: (index: number) => string = (index) => index.toString()
|
export let getKey: (index: number) => string = (index) => index.toString()
|
||||||
|
|
||||||
const refs: HTMLElement[] = []
|
const refs: HTMLElement[] = []
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
getPreviewAlignment,
|
getPreviewAlignment,
|
||||||
previewTypes
|
previewTypes
|
||||||
} from '@hcengineering/presentation'
|
} from '@hcengineering/presentation'
|
||||||
import { IconMoreH, IconOpen, Menu, Action as UIAction, closeTooltip, showPopup, tooltip } from '@hcengineering/ui'
|
import { IconMoreH, Menu, Action as UIAction, closeTooltip, showPopup, tooltip } from '@hcengineering/ui'
|
||||||
import view, { Action } from '@hcengineering/view'
|
import view, { Action } from '@hcengineering/view'
|
||||||
|
|
||||||
import AttachmentAction from './AttachmentAction.svelte'
|
import AttachmentAction from './AttachmentAction.svelte'
|
||||||
@ -84,7 +84,7 @@
|
|||||||
|
|
||||||
const openAction: UIAction = {
|
const openAction: UIAction = {
|
||||||
label: view.string.Open,
|
label: view.string.Open,
|
||||||
icon: IconOpen,
|
icon: view.icon.Open,
|
||||||
action: async (props: any, evt: Event) => {
|
action: async (props: any, evt: Event) => {
|
||||||
showPreview(evt as MouseEvent)
|
showPreview(evt as MouseEvent)
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@
|
|||||||
{#if canPreview}
|
{#if canPreview}
|
||||||
<AttachmentAction
|
<AttachmentAction
|
||||||
label={view.string.Open}
|
label={view.string.Open}
|
||||||
icon={IconOpen}
|
icon={view.icon.Open}
|
||||||
size="small"
|
size="small"
|
||||||
dataId="open-in-sidebar"
|
dataId="open-in-sidebar"
|
||||||
action={showPreview}
|
action={showPreview}
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
if (listProvider !== undefined) listProvider.updateFocus(value)
|
if (listProvider !== undefined) listProvider.updateFocus(value)
|
||||||
const popupInfo = showPopup(
|
const popupInfo = showPopup(
|
||||||
FilePreviewPopup,
|
FilePreviewPopup,
|
||||||
{ file: value.file, name: value.name, contentType: value.type },
|
{ file: value.file, name: value.name, contentType: value.type, metadata: value.metadata },
|
||||||
value.type.startsWith('image/') ? 'centered' : 'float'
|
value.type.startsWith('image/') ? 'centered' : 'float'
|
||||||
)
|
)
|
||||||
dispatch('open', popupInfo.id)
|
dispatch('open', popupInfo.id)
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
if (item !== undefined) {
|
if (item !== undefined) {
|
||||||
showPopup(
|
showPopup(
|
||||||
FilePreviewPopup,
|
FilePreviewPopup,
|
||||||
{ file: item.file, name: item.name, contentType: item.type },
|
{ file: item.file, name: item.name, contentType: item.type, metadata: item.metadata },
|
||||||
item.type.startsWith('image/') ? 'centered' : 'float'
|
item.type.startsWith('image/') ? 'centered' : 'float'
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -171,7 +171,8 @@
|
|||||||
|
|
||||||
const [id, _class] = decodeObjectURI(loc?.loc.path[3] ?? '')
|
const [id, _class] = decodeObjectURI(loc?.loc.path[3] ?? '')
|
||||||
const _id = await parseLinkId(linkProviders, id, _class)
|
const _id = await parseLinkId(linkProviders, id, _class)
|
||||||
const context = _id ? $contextByDocStore.get(_id) : undefined
|
const thread = loc?.loc.path[4] as Ref<ActivityMessage>
|
||||||
|
const context = $contextByDocStore.get(thread) ?? $contextByDocStore.get(_id)
|
||||||
|
|
||||||
selectedContextId = context?._id
|
selectedContextId = context?._id
|
||||||
|
|
||||||
@ -180,7 +181,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selectedMessageId = loc?.loc.query?.message as Ref<ActivityMessage> | undefined
|
const selectedMessageId = loc?.loc.query?.message as Ref<ActivityMessage> | undefined
|
||||||
const thread = loc?.loc.path[4] as Ref<ActivityMessage> | undefined
|
|
||||||
|
|
||||||
if (thread !== undefined) {
|
if (thread !== undefined) {
|
||||||
const fn = await getResource(chunter.function.OpenThreadInSidebar)
|
const fn = await getResource(chunter.function.OpenThreadInSidebar)
|
||||||
|
@ -93,6 +93,11 @@
|
|||||||
$: if (element != null) {
|
$: if (element != null) {
|
||||||
element.focus()
|
element.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getContextKey (index: number): string {
|
||||||
|
const contextId = displayData[index][0]
|
||||||
|
return contextId ?? index.toString()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
@ -108,6 +113,7 @@
|
|||||||
kind="full-size"
|
kind="full-size"
|
||||||
colorsSchema="lumia"
|
colorsSchema="lumia"
|
||||||
lazy={true}
|
lazy={true}
|
||||||
|
getKey={getContextKey}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="item" let:item={itemIndex}>
|
<svelte:fragment slot="item" let:item={itemIndex}>
|
||||||
{@const contextId = displayData[itemIndex][0]}
|
{@const contextId = displayData[itemIndex][0]}
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
Class,
|
Class,
|
||||||
Doc,
|
Doc,
|
||||||
DocumentQuery,
|
DocumentQuery,
|
||||||
|
type Domain,
|
||||||
IdMap,
|
IdMap,
|
||||||
Markup,
|
Markup,
|
||||||
Mixin,
|
Mixin,
|
||||||
@ -42,6 +43,10 @@ import { Readable, Writable } from './types'
|
|||||||
|
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
|
||||||
|
export const DOMAIN_NOTIFICATION = 'notification' as Domain
|
||||||
|
export const DOMAIN_DOC_NOTIFY = 'notification-dnc' as Domain
|
||||||
|
export const DOMAIN_USER_NOTIFY = 'notification-user' as Domain
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
|
@ -99,7 +99,7 @@
|
|||||||
|
|
||||||
function handleMouseDown (): void {
|
function handleMouseDown (): void {
|
||||||
function handleMouseMove (): void {
|
function handleMouseMove (): void {
|
||||||
if (!editor.state.selection.empty) {
|
if (editor !== undefined && !editor.state.selection.empty) {
|
||||||
selecting = true
|
selecting = true
|
||||||
document.removeEventListener('mousemove', handleMouseMove)
|
document.removeEventListener('mousemove', handleMouseMove)
|
||||||
}
|
}
|
||||||
@ -112,8 +112,10 @@
|
|||||||
document.removeEventListener('mouseup', handleMouseUp)
|
document.removeEventListener('mouseup', handleMouseUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('mousemove', handleMouseMove)
|
if (editor !== undefined) {
|
||||||
document.addEventListener('mouseup', handleMouseUp)
|
document.addEventListener('mousemove', handleMouseMove)
|
||||||
|
document.addEventListener('mouseup', handleMouseUp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
"dotenv": "~16.0.0",
|
"dotenv": "~16.0.0",
|
||||||
"@hcengineering/backup-service": "^0.6.0",
|
"@hcengineering/backup-service": "^0.6.0",
|
||||||
"@hcengineering/analytics": "^0.6.0",
|
"@hcengineering/analytics": "^0.6.0",
|
||||||
"@hcengineering/analytics-service": "^0.6.0"
|
"@hcengineering/analytics-service": "^0.6.0",
|
||||||
|
"@hcengineering/model-all": "^0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,19 @@
|
|||||||
import { Analytics } from '@hcengineering/analytics'
|
import { Analytics } from '@hcengineering/analytics'
|
||||||
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
|
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
|
||||||
import { startBackup } from '@hcengineering/backup-service'
|
import { startBackup } from '@hcengineering/backup-service'
|
||||||
import { MeasureMetricsContext, metricsToString, newMetrics } from '@hcengineering/core'
|
import { MeasureMetricsContext, metricsToString, newMetrics, type Tx } from '@hcengineering/core'
|
||||||
import { type PipelineFactory } from '@hcengineering/server-core'
|
import { type PipelineFactory } from '@hcengineering/server-core'
|
||||||
import { createBackupPipeline, getConfig } from '@hcengineering/server-pipeline'
|
import { createBackupPipeline, getConfig } from '@hcengineering/server-pipeline'
|
||||||
import { writeFile } from 'fs/promises'
|
import { writeFile } from 'fs/promises'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
|
import builder from '@hcengineering/model-all'
|
||||||
|
|
||||||
|
const enabled = (process.env.MODEL_ENABLED ?? '*').split(',').map((it) => it.trim())
|
||||||
|
const disabled = (process.env.MODEL_DISABLED ?? '').split(',').map((it) => it.trim())
|
||||||
|
|
||||||
|
const model = JSON.parse(JSON.stringify(builder(enabled, disabled).getTxes())) as Tx[]
|
||||||
|
|
||||||
const metricsContext = new MeasureMetricsContext(
|
const metricsContext = new MeasureMetricsContext(
|
||||||
'backup',
|
'backup',
|
||||||
{},
|
{},
|
||||||
@ -58,7 +65,7 @@ const onClose = (): void => {
|
|||||||
startBackup(
|
startBackup(
|
||||||
metricsContext,
|
metricsContext,
|
||||||
(mongoUrl, storageAdapter) => {
|
(mongoUrl, storageAdapter) => {
|
||||||
const factory: PipelineFactory = createBackupPipeline(metricsContext, mongoUrl, {
|
const factory: PipelineFactory = createBackupPipeline(metricsContext, mongoUrl, model, {
|
||||||
externalStorage: storageAdapter,
|
externalStorage: storageAdapter,
|
||||||
usePassedCtx: true
|
usePassedCtx: true
|
||||||
})
|
})
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
"@hcengineering/server-telegram": "^0.6.0",
|
"@hcengineering/server-telegram": "^0.6.0",
|
||||||
"@hcengineering/pod-telegram-bot": "^0.6.0",
|
"@hcengineering/pod-telegram-bot": "^0.6.0",
|
||||||
"@hcengineering/server-ai-bot": "^0.6.0",
|
"@hcengineering/server-ai-bot": "^0.6.0",
|
||||||
"@hcengineering/server-ai-bot-resources": "^0.6.0"
|
"@hcengineering/server-ai-bot-resources": "^0.6.0",
|
||||||
|
"@hcengineering/model-all": "^0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { type Branding, type BrandingMap, type WorkspaceIdWithUrl } from '@hcengineering/core'
|
import { type Branding, type BrandingMap, type Tx, type WorkspaceIdWithUrl } from '@hcengineering/core'
|
||||||
import { buildStorageFromConfig, getMetricsContext } from '@hcengineering/server'
|
import { buildStorageFromConfig, getMetricsContext } from '@hcengineering/server'
|
||||||
|
|
||||||
import { ClientSession, startSessionManager, type ServerFactory, type Session } from '@hcengineering/server'
|
import { ClientSession, startSessionManager, type ServerFactory, type Session } from '@hcengineering/server'
|
||||||
@ -25,6 +25,13 @@ import { serverAiBotId } from '@hcengineering/server-ai-bot'
|
|||||||
import { createAIBotAdapter } from '@hcengineering/server-ai-bot-resources'
|
import { createAIBotAdapter } from '@hcengineering/server-ai-bot-resources'
|
||||||
import { createServerPipeline, registerServerPlugins, registerStringLoaders } from '@hcengineering/server-pipeline'
|
import { createServerPipeline, registerServerPlugins, registerStringLoaders } from '@hcengineering/server-pipeline'
|
||||||
|
|
||||||
|
import builder from '@hcengineering/model-all'
|
||||||
|
|
||||||
|
const enabled = (process.env.MODEL_ENABLED ?? '*').split(',').map((it) => it.trim())
|
||||||
|
const disabled = (process.env.MODEL_DISABLED ?? '').split(',').map((it) => it.trim())
|
||||||
|
|
||||||
|
const model = JSON.parse(JSON.stringify(builder(enabled, disabled).getTxes())) as Tx[]
|
||||||
|
|
||||||
registerStringLoaders()
|
registerStringLoaders()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,6 +71,7 @@ export function start (
|
|||||||
const pipelineFactory = createServerPipeline(
|
const pipelineFactory = createServerPipeline(
|
||||||
metrics,
|
metrics,
|
||||||
dbUrls,
|
dbUrls,
|
||||||
|
model,
|
||||||
{ ...opt, externalStorage, adapterSecurity: rawDbUrl !== undefined },
|
{ ...opt, externalStorage, adapterSecurity: rawDbUrl !== undefined },
|
||||||
{
|
{
|
||||||
serviceAdapters: {
|
serviceAdapters: {
|
||||||
|
@ -31,6 +31,7 @@ import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core'
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export const serverNotificationId = 'server-notification' as Plugin
|
export const serverNotificationId = 'server-notification' as Plugin
|
||||||
|
export { DOMAIN_USER_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_DOC_NOTIFY } from '@hcengineering/notification'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"@hcengineering/platform": "^0.6.11",
|
"@hcengineering/platform": "^0.6.11",
|
||||||
"@hcengineering/server-core": "^0.6.1",
|
"@hcengineering/server-core": "^0.6.1",
|
||||||
"@hcengineering/server-preference": "^0.6.0",
|
"@hcengineering/server-preference": "^0.6.0",
|
||||||
|
"@hcengineering/server-notification": "^0.6.1",
|
||||||
"@hcengineering/query": "^0.6.12",
|
"@hcengineering/query": "^0.6.12",
|
||||||
"fast-equals": "^5.0.1"
|
"fast-equals": "^5.0.1"
|
||||||
}
|
}
|
||||||
|
@ -33,3 +33,4 @@ export * from './spacePermissions'
|
|||||||
export * from './spaceSecurity'
|
export * from './spaceSecurity'
|
||||||
export * from './triggers'
|
export * from './triggers'
|
||||||
export * from './txPush'
|
export * from './txPush'
|
||||||
|
export * from './notifications'
|
||||||
|
@ -14,14 +14,22 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import core, {
|
import core, {
|
||||||
|
type Doc,
|
||||||
type LoadModelResponse,
|
type LoadModelResponse,
|
||||||
type MeasureContext,
|
type MeasureContext,
|
||||||
type Timestamp,
|
type Timestamp,
|
||||||
type Tx,
|
type Tx,
|
||||||
|
type TxCUD,
|
||||||
DOMAIN_TX
|
DOMAIN_TX
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { PlatformError, unknownError } from '@hcengineering/platform'
|
import { PlatformError, unknownError } from '@hcengineering/platform'
|
||||||
import type { Middleware, PipelineContext, TxAdapter, TxMiddlewareResult } from '@hcengineering/server-core'
|
import type {
|
||||||
|
Middleware,
|
||||||
|
MiddlewareCreator,
|
||||||
|
PipelineContext,
|
||||||
|
TxAdapter,
|
||||||
|
TxMiddlewareResult
|
||||||
|
} from '@hcengineering/server-core'
|
||||||
import { BaseMiddleware } from '@hcengineering/server-core'
|
import { BaseMiddleware } from '@hcengineering/server-core'
|
||||||
import crypto from 'node:crypto'
|
import crypto from 'node:crypto'
|
||||||
|
|
||||||
@ -34,20 +42,46 @@ export class ModelMiddleware extends BaseMiddleware implements Middleware {
|
|||||||
lastHashResponse!: Promise<LoadModelResponse>
|
lastHashResponse!: Promise<LoadModelResponse>
|
||||||
model!: Tx[]
|
model!: Tx[]
|
||||||
|
|
||||||
static async create (ctx: MeasureContext, context: PipelineContext, next?: Middleware): Promise<Middleware> {
|
constructor (
|
||||||
const middleware = new ModelMiddleware(context, next)
|
context: PipelineContext,
|
||||||
|
next: Middleware | undefined,
|
||||||
|
readonly systemTx: Tx[]
|
||||||
|
) {
|
||||||
|
super(context, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
static async doCreate (
|
||||||
|
ctx: MeasureContext,
|
||||||
|
context: PipelineContext,
|
||||||
|
next: Middleware | undefined,
|
||||||
|
systemTx: Tx[]
|
||||||
|
): Promise<Middleware> {
|
||||||
|
const middleware = new ModelMiddleware(context, next, systemTx)
|
||||||
await middleware.init(ctx)
|
await middleware.init(ctx)
|
||||||
return middleware
|
return middleware
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static create (tx: Tx[]): MiddlewareCreator {
|
||||||
|
return (ctx, context, next) => {
|
||||||
|
return this.doCreate(ctx, context, next, tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async init (ctx: MeasureContext): Promise<void> {
|
async init (ctx: MeasureContext): Promise<void> {
|
||||||
if (this.context.adapterManager == null) {
|
if (this.context.adapterManager == null) {
|
||||||
throw new PlatformError(unknownError('Adapter manager should be configured'))
|
throw new PlatformError(unknownError('Adapter manager should be configured'))
|
||||||
}
|
}
|
||||||
const txAdapter = this.context.adapterManager.getAdapter(DOMAIN_TX, true) as TxAdapter
|
const txAdapter = this.context.adapterManager.getAdapter(DOMAIN_TX, true) as TxAdapter
|
||||||
|
|
||||||
|
const isUserTx = (it: Tx): boolean =>
|
||||||
|
it.modifiedBy !== core.account.System ||
|
||||||
|
(it as TxCUD<Doc>).objectClass === 'contact:class:Person' ||
|
||||||
|
(it as TxCUD<Doc>).objectClass === 'contact:class:PersonAccount'
|
||||||
|
|
||||||
this.model = await ctx.with('get-model', {}, async (ctx) => {
|
this.model = await ctx.with('get-model', {}, async (ctx) => {
|
||||||
const model = await ctx.with('fetch-model', {}, (ctx) => txAdapter.getModel(ctx))
|
const allUserTxes = await ctx.with('fetch-model', {}, (ctx) => txAdapter.getModel(ctx))
|
||||||
|
const userTxes = allUserTxes.filter((it) => isUserTx(it))
|
||||||
|
const model = this.systemTx.concat(userTxes)
|
||||||
for (const tx of model) {
|
for (const tx of model) {
|
||||||
try {
|
try {
|
||||||
this.context.hierarchy.tx(tx)
|
this.context.hierarchy.tx(tx)
|
||||||
@ -55,7 +89,7 @@ export class ModelMiddleware extends BaseMiddleware implements Middleware {
|
|||||||
ctx.warn('failed to apply model transaction, skipping', { tx: JSON.stringify(tx), err })
|
ctx.warn('failed to apply model transaction, skipping', { tx: JSON.stringify(tx), err })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.context.modelDb.addTxes(ctx, model, false)
|
this.context.modelDb.addTxes(ctx, model, true)
|
||||||
return model
|
return model
|
||||||
})
|
})
|
||||||
|
|
||||||
|
97
server/middleware/src/notifications.ts
Normal file
97
server/middleware/src/notifications.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2024 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import core, {
|
||||||
|
Doc,
|
||||||
|
MeasureContext,
|
||||||
|
Tx,
|
||||||
|
TxCUD,
|
||||||
|
TxProcessor,
|
||||||
|
systemAccountEmail,
|
||||||
|
type SessionData,
|
||||||
|
TxApplyIf
|
||||||
|
} from '@hcengineering/core'
|
||||||
|
import platform, { PlatformError, Severity, Status } from '@hcengineering/platform'
|
||||||
|
import { BaseMiddleware, Middleware, TxMiddlewareResult, type PipelineContext } from '@hcengineering/server-core'
|
||||||
|
import { DOMAIN_USER_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_DOC_NOTIFY } from '@hcengineering/server-notification'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export class NotificationsMiddleware extends BaseMiddleware implements Middleware {
|
||||||
|
private readonly targetDomains = [DOMAIN_USER_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_DOC_NOTIFY]
|
||||||
|
|
||||||
|
private constructor (context: PipelineContext, next?: Middleware) {
|
||||||
|
super(context, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create (
|
||||||
|
ctx: MeasureContext,
|
||||||
|
context: PipelineContext,
|
||||||
|
next: Middleware | undefined
|
||||||
|
): Promise<NotificationsMiddleware> {
|
||||||
|
return new NotificationsMiddleware(context, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
isTargetDomain (tx: Tx): boolean {
|
||||||
|
if (TxProcessor.isExtendsCUD(tx._class)) {
|
||||||
|
const txCUD = tx as TxCUD<Doc>
|
||||||
|
const domain = this.context.hierarchy.getDomain(txCUD.objectClass)
|
||||||
|
return this.targetDomains.includes(domain)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
processTx (ctx: MeasureContext<SessionData>, tx: Tx): void {
|
||||||
|
let target: string[] | undefined
|
||||||
|
if (this.isTargetDomain(tx)) {
|
||||||
|
const account = ctx.contextData.account._id
|
||||||
|
if (account !== tx.modifiedBy && account !== core.account.System) {
|
||||||
|
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
|
||||||
|
}
|
||||||
|
const modifiedByAccount = ctx.contextData.getAccount(tx.modifiedBy)
|
||||||
|
target = [ctx.contextData.userEmail, systemAccountEmail]
|
||||||
|
if (modifiedByAccount !== undefined && !target.includes(modifiedByAccount.email)) {
|
||||||
|
target.push(modifiedByAccount.email)
|
||||||
|
}
|
||||||
|
ctx.contextData.broadcast.targets['checkDomain' + account] = (tx) => {
|
||||||
|
if (this.isTargetDomain(tx)) {
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx (ctx: MeasureContext<SessionData>, txes: Tx[]): Promise<TxMiddlewareResult> {
|
||||||
|
for (const tx of txes) {
|
||||||
|
if (this.context.hierarchy.isDerived(tx._class, core.class.TxApplyIf)) {
|
||||||
|
for (const ttx of (tx as TxApplyIf).txes) {
|
||||||
|
this.processTx(ctx, ttx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.processTx(ctx, tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.provideTx(ctx, txes)
|
||||||
|
}
|
||||||
|
|
||||||
|
isAvailable (ctx: MeasureContext<SessionData>, doc: Doc): boolean {
|
||||||
|
const domain = this.context.hierarchy.getDomain(doc._class)
|
||||||
|
if (!this.targetDomains.includes(domain)) return true
|
||||||
|
const account = ctx.contextData.account._id
|
||||||
|
return doc.createdBy === account || account === core.account.System
|
||||||
|
}
|
||||||
|
}
|
@ -1174,7 +1174,9 @@ abstract class MongoAdapterBase implements DbAdapter {
|
|||||||
|
|
||||||
async clean (ctx: MeasureContext, domain: Domain, docs: Ref<Doc>[]): Promise<void> {
|
async clean (ctx: MeasureContext, domain: Domain, docs: Ref<Doc>[]): Promise<void> {
|
||||||
await ctx.with('clean', {}, async () => {
|
await ctx.with('clean', {}, async () => {
|
||||||
await this.db.collection<Doc>(domain).deleteMany({ _id: { $in: docs } })
|
if (docs.length > 0) {
|
||||||
|
await this.db.collection<Doc>(domain).deleteMany({ _id: { $in: docs } })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1693,7 +1695,7 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
model.forEach((tx) => (tx.modifiedBy === core.account.System && !isPersonAccount(tx) ? systemTx : userTx).push(tx))
|
model.forEach((tx) => (tx.modifiedBy === core.account.System && !isPersonAccount(tx) ? systemTx : userTx).push(tx))
|
||||||
return systemTx.concat(userTx)
|
return this.stripHash(systemTx.concat(userTx)) as Tx[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +169,7 @@ export class DBCollectionHelper implements DomainHelperOperations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async init (domain?: Domain): Promise<void> {
|
async init (domain?: Domain): Promise<void> {
|
||||||
|
// Check and create DB if missin
|
||||||
if (domain === undefined) {
|
if (domain === undefined) {
|
||||||
// Init existing collecfions
|
// Init existing collecfions
|
||||||
for (const c of (await this.db.listCollections({}, { nameOnly: true }).toArray()).map((it) => it.name)) {
|
for (const c of (await this.db.listCollections({}, { nameOnly: true }).toArray()).map((it) => it.name)) {
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
ModelDb,
|
ModelDb,
|
||||||
type Branding,
|
type Branding,
|
||||||
type MeasureContext,
|
type MeasureContext,
|
||||||
|
type Tx,
|
||||||
type WorkspaceIdWithUrl
|
type WorkspaceIdWithUrl
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { createElasticAdapter, createElasticBackupDataAdapter } from '@hcengineering/elastic'
|
import { createElasticAdapter, createElasticBackupDataAdapter } from '@hcengineering/elastic'
|
||||||
@ -33,7 +34,8 @@ import {
|
|||||||
SpacePermissionsMiddleware,
|
SpacePermissionsMiddleware,
|
||||||
SpaceSecurityMiddleware,
|
SpaceSecurityMiddleware,
|
||||||
TriggersMiddleware,
|
TriggersMiddleware,
|
||||||
TxMiddleware
|
TxMiddleware,
|
||||||
|
NotificationsMiddleware
|
||||||
} from '@hcengineering/middleware'
|
} from '@hcengineering/middleware'
|
||||||
import { createMongoAdapter, createMongoTxAdapter } from '@hcengineering/mongo'
|
import { createMongoAdapter, createMongoTxAdapter } from '@hcengineering/mongo'
|
||||||
import { createPostgresAdapter, createPostgresTxAdapter } from '@hcengineering/postgres'
|
import { createPostgresAdapter, createPostgresTxAdapter } from '@hcengineering/postgres'
|
||||||
@ -99,6 +101,7 @@ export function getTxAdapterFactory (
|
|||||||
export function createServerPipeline (
|
export function createServerPipeline (
|
||||||
metrics: MeasureContext,
|
metrics: MeasureContext,
|
||||||
dbUrls: string,
|
dbUrls: string,
|
||||||
|
model: Tx[],
|
||||||
opt: {
|
opt: {
|
||||||
fullTextUrl: string
|
fullTextUrl: string
|
||||||
rekoniUrl: string
|
rekoniUrl: string
|
||||||
@ -121,6 +124,7 @@ export function createServerPipeline (
|
|||||||
LookupMiddleware.create,
|
LookupMiddleware.create,
|
||||||
ModifiedMiddleware.create,
|
ModifiedMiddleware.create,
|
||||||
PrivateMiddleware.create,
|
PrivateMiddleware.create,
|
||||||
|
NotificationsMiddleware.create,
|
||||||
(ctx: MeasureContext, context: PipelineContext, next?: Middleware) =>
|
(ctx: MeasureContext, context: PipelineContext, next?: Middleware) =>
|
||||||
SpaceSecurityMiddleware.create(opt.adapterSecurity ?? false, ctx, context, next),
|
SpaceSecurityMiddleware.create(opt.adapterSecurity ?? false, ctx, context, next),
|
||||||
SpacePermissionsMiddleware.create,
|
SpacePermissionsMiddleware.create,
|
||||||
@ -137,7 +141,7 @@ export function createServerPipeline (
|
|||||||
DomainFindMiddleware.create,
|
DomainFindMiddleware.create,
|
||||||
DomainTxMiddleware.create,
|
DomainTxMiddleware.create,
|
||||||
DBAdapterInitMiddleware.create,
|
DBAdapterInitMiddleware.create,
|
||||||
ModelMiddleware.create,
|
ModelMiddleware.create(model),
|
||||||
DBAdapterMiddleware.create(conf), // Configure DB adapters
|
DBAdapterMiddleware.create(conf), // Configure DB adapters
|
||||||
BroadcastMiddleware.create(broadcast)
|
BroadcastMiddleware.create(broadcast)
|
||||||
]
|
]
|
||||||
@ -162,6 +166,7 @@ export function createServerPipeline (
|
|||||||
export function createBackupPipeline (
|
export function createBackupPipeline (
|
||||||
metrics: MeasureContext,
|
metrics: MeasureContext,
|
||||||
dbUrls: string,
|
dbUrls: string,
|
||||||
|
systemTx: Tx[],
|
||||||
opt: {
|
opt: {
|
||||||
usePassedCtx?: boolean
|
usePassedCtx?: boolean
|
||||||
adapterSecurity?: boolean
|
adapterSecurity?: boolean
|
||||||
@ -206,7 +211,7 @@ export function createBackupPipeline (
|
|||||||
ContextNameMiddleware.create,
|
ContextNameMiddleware.create,
|
||||||
DomainFindMiddleware.create,
|
DomainFindMiddleware.create,
|
||||||
DBAdapterInitMiddleware.create,
|
DBAdapterInitMiddleware.create,
|
||||||
ModelMiddleware.create,
|
ModelMiddleware.create(systemTx),
|
||||||
DBAdapterMiddleware.create(conf)
|
DBAdapterMiddleware.create(conf)
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -225,6 +230,7 @@ export function createBackupPipeline (
|
|||||||
|
|
||||||
export async function getServerPipeline (
|
export async function getServerPipeline (
|
||||||
ctx: MeasureContext,
|
ctx: MeasureContext,
|
||||||
|
model: Tx[],
|
||||||
mongodbUri: string,
|
mongodbUri: string,
|
||||||
dbUrl: string | undefined,
|
dbUrl: string | undefined,
|
||||||
wsUrl: WorkspaceIdWithUrl
|
wsUrl: WorkspaceIdWithUrl
|
||||||
@ -240,6 +246,7 @@ export async function getServerPipeline (
|
|||||||
const pipelineFactory = createServerPipeline(
|
const pipelineFactory = createServerPipeline(
|
||||||
ctx,
|
ctx,
|
||||||
dbUrls,
|
dbUrls,
|
||||||
|
model,
|
||||||
{
|
{
|
||||||
externalStorage: storageAdapter,
|
externalStorage: storageAdapter,
|
||||||
fullTextUrl: 'http://localhost:9200',
|
fullTextUrl: 'http://localhost:9200',
|
||||||
|
@ -36,15 +36,16 @@ import core, {
|
|||||||
WorkspaceId,
|
WorkspaceId,
|
||||||
WorkspaceIdWithUrl,
|
WorkspaceIdWithUrl,
|
||||||
type Doc,
|
type Doc,
|
||||||
|
type Ref,
|
||||||
type TxCUD
|
type TxCUD
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { consoleModelLogger, MigrateOperation, ModelLogger, tryMigrate } from '@hcengineering/model'
|
import { consoleModelLogger, MigrateOperation, ModelLogger, tryMigrate } from '@hcengineering/model'
|
||||||
import {
|
import {
|
||||||
AggregatorStorageAdapter,
|
AggregatorStorageAdapter,
|
||||||
DbAdapter,
|
|
||||||
DomainIndexHelperImpl,
|
DomainIndexHelperImpl,
|
||||||
Pipeline,
|
Pipeline,
|
||||||
StorageAdapter
|
StorageAdapter,
|
||||||
|
type DbAdapter
|
||||||
} from '@hcengineering/server-core'
|
} from '@hcengineering/server-core'
|
||||||
import { connect } from './connect'
|
import { connect } from './connect'
|
||||||
import { InitScript, WorkspaceInitializer } from './initializer'
|
import { InitScript, WorkspaceInitializer } from './initializer'
|
||||||
@ -141,9 +142,16 @@ export async function initModel (
|
|||||||
logger.log('transactions deleted.', { workspaceId: workspaceId.name })
|
logger.log('transactions deleted.', { workspaceId: workspaceId.name })
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('creating model...', workspaceId)
|
logger.log('creating database...', workspaceId)
|
||||||
await adapter.upload(ctx, DOMAIN_TX, txes)
|
await adapter.upload(ctx, DOMAIN_TX, [
|
||||||
logger.log('model transactions inserted.', { count: txes.length })
|
{
|
||||||
|
_class: core.class.Tx,
|
||||||
|
_id: 'first-tx' as Ref<Doc>,
|
||||||
|
modifiedBy: core.account.System,
|
||||||
|
modifiedOn: Date.now(),
|
||||||
|
space: core.space.DerivedTx
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
await progress(30)
|
await progress(30)
|
||||||
|
|
||||||
@ -264,11 +272,14 @@ export async function upgradeModel (
|
|||||||
throw Error('Model txes must target only core.space.Model')
|
throw Error('Model txes must target only core.space.Model')
|
||||||
}
|
}
|
||||||
|
|
||||||
const prevModel = await fetchModel(ctx, pipeline)
|
const newModelRes = await ctx.with('load-model', {}, (ctx) => pipeline.loadModel(ctx, 0))
|
||||||
|
const newModel = Array.isArray(newModelRes) ? newModelRes : newModelRes.transactions
|
||||||
|
|
||||||
|
const { hierarchy, modelDb, model } = await buildModel(ctx, newModel)
|
||||||
const { migrateClient: preMigrateClient } = await prepareMigrationClient(
|
const { migrateClient: preMigrateClient } = await prepareMigrationClient(
|
||||||
pipeline,
|
pipeline,
|
||||||
prevModel.hierarchy,
|
hierarchy,
|
||||||
prevModel.modelDb,
|
modelDb,
|
||||||
logger,
|
logger,
|
||||||
storageAdapter,
|
storageAdapter,
|
||||||
workspaceId
|
workspaceId
|
||||||
@ -305,36 +316,9 @@ export async function upgradeModel (
|
|||||||
}
|
}
|
||||||
logger.log('removing model...', { workspaceId: workspaceId.name })
|
logger.log('removing model...', { workspaceId: workspaceId.name })
|
||||||
await progress(10)
|
await progress(10)
|
||||||
const toRemove = await pipeline.findAll(ctx, core.class.Tx, {
|
|
||||||
objectSpace: core.space.Model,
|
|
||||||
modifiedBy: core.account.System,
|
|
||||||
objectClass: { $nin: [contact.class.PersonAccount, 'contact:class:EmployeeAccount'] }
|
|
||||||
})
|
|
||||||
await pipeline.context.lowLevelStorage.clean(
|
|
||||||
ctx,
|
|
||||||
DOMAIN_TX,
|
|
||||||
toRemove.map((p) => p._id)
|
|
||||||
)
|
|
||||||
logger.log('transactions deleted.', { workspaceId: workspaceId.name, count: toRemove.length })
|
|
||||||
logger.log('creating model...', { workspaceId: workspaceId.name })
|
|
||||||
await pipeline.context.lowLevelStorage.upload(ctx, DOMAIN_TX, txes)
|
|
||||||
|
|
||||||
logger.log('model transactions inserted.', { workspaceId: workspaceId.name, count: txes.length })
|
logger.log('model transactions inserted.', { workspaceId: workspaceId.name, count: txes.length })
|
||||||
await progress(20)
|
await progress(20)
|
||||||
}
|
}
|
||||||
const newModel = [
|
|
||||||
...txes,
|
|
||||||
...Array.from(
|
|
||||||
prevModel.model.filter(
|
|
||||||
(it) =>
|
|
||||||
it.modifiedBy !== core.account.System ||
|
|
||||||
(it as TxCUD<Doc>).objectClass === contact.class.Person ||
|
|
||||||
(it as TxCUD<Doc>).objectClass === 'contact:class:PersonAccount'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
const { hierarchy, modelDb, model } = await fetchModel(ctx, pipeline, newModel)
|
|
||||||
const { migrateClient, migrateState } = await prepareMigrationClient(
|
const { migrateClient, migrateState } = await prepareMigrationClient(
|
||||||
pipeline,
|
pipeline,
|
||||||
hierarchy,
|
hierarchy,
|
||||||
@ -383,6 +367,21 @@ export async function upgradeModel (
|
|||||||
{
|
{
|
||||||
state: 'indexes-v4',
|
state: 'indexes-v4',
|
||||||
func: upgradeIndexes
|
func: upgradeIndexes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
state: 'delete-model',
|
||||||
|
func: async (client) => {
|
||||||
|
const model = await client.find<Tx>(DOMAIN_TX, { objectSpace: core.space.Model })
|
||||||
|
|
||||||
|
// Ignore Employee accounts.
|
||||||
|
const isUserTx = (it: Tx): boolean =>
|
||||||
|
it.modifiedBy !== core.account.System ||
|
||||||
|
(it as TxCUD<Doc>).objectClass === 'contact:class:Person' ||
|
||||||
|
(it as TxCUD<Doc>).objectClass === 'contact:class:PersonAccount'
|
||||||
|
|
||||||
|
const toDelete = model.filter((it) => !isUserTx(it)).map((it) => it._id)
|
||||||
|
await client.deleteMany(DOMAIN_TX, { _id: { $in: toDelete } })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
@ -463,26 +462,20 @@ async function prepareMigrationClient (
|
|||||||
return { migrateClient, migrateState }
|
return { migrateClient, migrateState }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchModel (
|
export async function buildModel (
|
||||||
ctx: MeasureContext,
|
ctx: MeasureContext,
|
||||||
pipeline: Pipeline,
|
model: Tx[]
|
||||||
model?: Tx[]
|
|
||||||
): Promise<{ hierarchy: Hierarchy, modelDb: ModelDb, model: Tx[] }> {
|
): Promise<{ hierarchy: Hierarchy, modelDb: ModelDb, model: Tx[] }> {
|
||||||
const hierarchy = new Hierarchy()
|
const hierarchy = new Hierarchy()
|
||||||
const modelDb = new ModelDb(hierarchy)
|
const modelDb = new ModelDb(hierarchy)
|
||||||
|
|
||||||
if (model === undefined) {
|
|
||||||
const res = await ctx.with('load-model', {}, (ctx) => pipeline.loadModel(ctx, 0))
|
|
||||||
model = Array.isArray(res) ? res : res.transactions
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.withSync('build local model', {}, () => {
|
ctx.withSync('build local model', {}, () => {
|
||||||
for (const tx of model ?? []) {
|
for (const tx of model ?? []) {
|
||||||
try {
|
try {
|
||||||
hierarchy.tx(tx)
|
hierarchy.tx(tx)
|
||||||
} catch (err: any) {}
|
} catch (err: any) {}
|
||||||
}
|
}
|
||||||
modelDb.addTxes(ctx, model as Tx[], false)
|
modelDb.addTxes(ctx, model, false)
|
||||||
})
|
})
|
||||||
return { hierarchy, modelDb, model: model ?? [] }
|
return { hierarchy, modelDb, model: model ?? [] }
|
||||||
}
|
}
|
||||||
|
@ -163,6 +163,7 @@ export async function createWorkspace (
|
|||||||
const factory: PipelineFactory = createServerPipeline(
|
const factory: PipelineFactory = createServerPipeline(
|
||||||
ctx,
|
ctx,
|
||||||
dbUrls,
|
dbUrls,
|
||||||
|
txes,
|
||||||
{
|
{
|
||||||
externalStorage: storageAdapter,
|
externalStorage: storageAdapter,
|
||||||
fullTextUrl: 'http://localhost:9200',
|
fullTextUrl: 'http://localhost:9200',
|
||||||
@ -288,7 +289,7 @@ export async function upgradeWorkspace (
|
|||||||
let pipeline: Pipeline | undefined
|
let pipeline: Pipeline | undefined
|
||||||
let storageAdapter: StorageAdapter | undefined
|
let storageAdapter: StorageAdapter | undefined
|
||||||
try {
|
try {
|
||||||
;({ pipeline, storageAdapter } = await getServerPipeline(ctx, mongodbUri, dbUrl, wsUrl))
|
;({ pipeline, storageAdapter } = await getServerPipeline(ctx, txes, mongodbUri, dbUrl, wsUrl))
|
||||||
const contextData = new SessionDataImpl(
|
const contextData = new SessionDataImpl(
|
||||||
systemAccountEmail,
|
systemAccountEmail,
|
||||||
'backup',
|
'backup',
|
||||||
|
Loading…
Reference in New Issue
Block a user