Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion models/tracker/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,34 @@ export function createActions (builder: Builder, issuesId: string, componentsId:
mode: ['browser'],
application: tracker.app.Tracker,
group: 'create'
}
},
override: [tracker.action.NewIssueGlobal]
},
tracker.action.NewIssue
)

createAction(
builder,
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: tracker.component.CreateIssue,
element: 'top'
},
label: tracker.string.NewIssue,
icon: tracker.icon.NewIssue,
keyBinding: [],
input: 'none',
category: tracker.category.Tracker,
target: core.class.Doc,
context: {
mode: [],
group: 'create'
}
},
tracker.action.NewIssueGlobal
)

createAction(
builder,
{
Expand Down
20 changes: 19 additions & 1 deletion models/workbench/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { Class, DOMAIN_MODEL, Ref, Space } from '@hcengineering/core'
import { Builder, Mixin, Model, Prop, TypeRef, UX } from '@hcengineering/model'
import preference, { TPreference } from '@hcengineering/model-preference'
import { createAction } from '@hcengineering/model-view'
import type { Asset, IntlString } from '@hcengineering/platform'
import { getEmbeddedLabel, type Asset, type IntlString } from '@hcengineering/platform'
import view, { KeyBinding } from '@hcengineering/view'
import type {
Application,
Expand Down Expand Up @@ -69,6 +69,24 @@ export function createModel (builder: Builder): void {
builder.mixin(workbench.class.Application, core.class.Class, view.mixin.IgnoreActions, {
actions: [view.action.Delete]
})

createAction(builder, {
action: view.actionImpl.ShowPopup,
actionProps: {
component: workbench.component.ServerManager,
element: 'content'
},
label: getEmbeddedLabel('Server statistics'),
icon: view.icon.Configure,
keyBinding: [],
input: 'none',
category: view.category.General,
target: core.class.Doc,
secured: true,
context: {
mode: ['workbench']
}
})
}

export default workbench
Expand Down
3 changes: 2 additions & 1 deletion models/workbench/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export default mergeIds(workbenchId, workbench, {
ApplicationPresenter: '' as AnyComponent,
Archive: '' as AnyComponent,
SpaceBrowser: '' as AnyComponent,
SpecialView: '' as AnyComponent
SpecialView: '' as AnyComponent,
ServerManager: '' as AnyComponent
},
string: {
Application: '' as IntlString,
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/components/Separator.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@
}

const checkSibling = (start: boolean = false): void => {
if (separator === null) return
if (prevElement === null || start) prevElement = separator.previousElementSibling as HTMLElement
if (nextElement === null || start) nextElement = separator.nextElementSibling as HTMLElement
if (separators && prevElement && separators[index].float !== undefined) {
Expand Down
5 changes: 1 addition & 4 deletions packages/ui/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,7 @@ export const uis = plugin(uiId, {
metadata: {
DefaultApplication: '' as Metadata<AnyComponent>,
Routes: '' as Metadata<Map<string, AnyComponent>>,
Languages: '' as Metadata<string[]>,

// Will activate network click button
ShowNetwork: '' as Metadata<(evt: MouseEvent) => void>
Languages: '' as Metadata<string[]>
}
})

Expand Down
1 change: 1 addition & 0 deletions plugins/tracker/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ export default plugin(trackerId, {
Duplicate: '' as Ref<Action>,
Relations: '' as Ref<Action>,
NewIssue: '' as Ref<Action>,
NewIssueGlobal: '' as Ref<Action>,
NewSubIssue: '' as Ref<Action>,
EditWorkflowStatuses: '' as Ref<Action>,
EditProject: '' as Ref<Action>,
Expand Down
106 changes: 79 additions & 27 deletions plugins/workbench-resources/src/components/ServerManager.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
<script lang="ts">
import contact, { PersonAccount } from '@hcengineering/contact'
import { metricsToRows } from '@hcengineering/core'
import { getEmbeddedLabel } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import { Button, IconArrowRight, Loading, Panel, Scroller, TabItem, TabList, ticker } from '@hcengineering/ui'
import login from '@hcengineering/login'
import { getEmbeddedLabel, getMetadata } from '@hcengineering/platform'
import presentation, { createQuery } from '@hcengineering/presentation'
import {
Button,
IconArrowRight,
Loading,
Panel,
Scroller,
TabItem,
TabList,
closePopup,
fetchMetadataLocalStorage,
ticker
} from '@hcengineering/ui'
import EditBox from '@hcengineering/ui/src/components/EditBox.svelte'
import Expandable from '@hcengineering/ui/src/components/Expandable.svelte'
import { ObjectPresenter } from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'

export let endpoint: string
export let token: string
const _endpoint: string = fetchMetadataLocalStorage(login.metadata.LoginEndpoint) ?? ''
const token: string = getMetadata(presentation.metadata.Token) ?? ''

let endpoint = _endpoint.replace(/^ws/g, 'http')
if (endpoint.endsWith('/')) {
endpoint = endpoint.substring(0, endpoint.length - 1)
}

let data: any
let admin = false
Expand All @@ -18,6 +36,9 @@
fetch(endpoint + `/api/v1/statistics?token=${token}`, {}).then(async (json) => {
data = await json.json()
admin = data?.admin ?? false
if (admin === false) {
closePopup()
}
})
})
)
Expand Down Expand Up @@ -47,6 +68,7 @@
string,
{
userId: string
data?: Record<string, any>
total: StatisticsElement
mins5: StatisticsElement
current: StatisticsElement
Expand Down Expand Up @@ -125,33 +147,63 @@
{:else if selectedTab === 'users'}
<div class="flex-column p-3 h-full" style:overflow="auto">
{#each Object.entries(activeSessions) as act}
{@const totalFind = act[1].reduce((it, itm) => itm.current.find + it, 0)}
{@const totalTx = act[1].reduce((it, itm) => itm.current.tx + it, 0)}
{@const employeeGroups = Array.from(new Set(act[1].map((it) => it.userId)))}
<span class="flex-col">
<div class="fs-title">
Workspace: {act[0]}: {act[1].length}
Workspace: {act[0]}: {act[1].length} current 5 mins => {totalFind}/{totalTx}
</div>

<div class="flex-col">
{#each act[1] as user}
{@const employee = employees.get(user.userId)}
<div class="p-1 flex-row-center">
{#if employee}
<ObjectPresenter
_class={contact.mixin.Employee}
objectId={employee.person}
props={{ shouldShowAvatar: true }}
/>
{:else}
{user.userId}
{/if}
<div class="p-1">
Total: {user.total.find}/{user.total.tx}
</div>
<div class="p-1">
Previous 5 mins: {user.mins5.find}/{user.mins5.tx}
</div>
<div class="p-1">
Current 5 mins: {user.current.find}/{user.current.tx}
</div>
{#each employeeGroups as employeeId}
{@const employee = employees.get(employeeId)}
{@const connections = act[1].filter((it) => it.userId === employeeId)}

{@const find = connections.reduce((it, itm) => itm.current.find + it, 0)}
{@const txes = connections.reduce((it, itm) => itm.current.tx + it, 0)}
<div class="p-1 flex-col">
<Expandable>
<svelte:fragment slot="title">
<div class="flex-row-center p-1">
{#if employee}
<ObjectPresenter
_class={contact.mixin.Employee}
objectId={employee.person}
props={{ shouldShowAvatar: true }}
/>
{:else}
{employeeId}
{/if}
: {connections.length}
<div class="ml-4">
<div class="ml-1">{find}/{txes}</div>
</div>
</div>
</svelte:fragment>
{#each connections as user, i}
<div class="flex-row-center ml-10">
#{i}
{user.userId}
<div class="p-1">
Total: {user.total.find}/{user.total.tx}
</div>
<div class="p-1">
Previous 5 mins: {user.mins5.find}/{user.mins5.tx}
</div>
<div class="p-1">
Current 5 mins: {user.current.find}/{user.current.tx}
</div>
</div>
<div class="p-1 flex-col ml-10">
{#each Object.entries(user.data ?? {}) as [k, v]}
<div class="p-1">
{k}: {JSON.stringify(v)}
</div>
{/each}
</div>
{/each}
</Expandable>
</div>
{/each}
</div>
Expand Down
25 changes: 2 additions & 23 deletions plugins/workbench-resources/src/connect.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import client from '@hcengineering/client'
import core, {
AccountClient,
AccountRole,
Client,
ClientConnectEvent,
Version,
Expand All @@ -12,15 +11,13 @@ import core, {
import login, { loginId } from '@hcengineering/login'
import { addEventListener, broadcastEvent, getMetadata, getResource, setMetadata } from '@hcengineering/platform'
import presentation, { closeClient, refreshClient, setClient } from '@hcengineering/presentation'
import ui, {
import {
fetchMetadataLocalStorage,
getCurrentLocation,
navigate,
networkStatus,
setMetadataLocalStorage,
showPopup
setMetadataLocalStorage
} from '@hcengineering/ui'
import ServerManager from './components/ServerManager.svelte'
import plugin from './plugin'

export let versionError: string | undefined = ''
Expand Down Expand Up @@ -186,24 +183,6 @@ export async function connect (title: string): Promise<Client | undefined> {
document.title = [ws, title].filter((it) => it).join(' - ')
_clientSet = true
await setClient(_client)

if (me.role === AccountRole.Owner) {
setMetadata(ui.metadata.ShowNetwork, (evt: MouseEvent) => {
if (getMetadata(presentation.metadata.Token) == null) {
return
}
if (getCurrentAccount()?.role === AccountRole.Owner) {
showPopup(
ServerManager,
{
endpoint: serverEndpoint,
token
},
'content'
)
}
})
}
await broadcastEvent(plugin.event.NotifyConnection, getCurrentAccount())

return _client
Expand Down
4 changes: 3 additions & 1 deletion plugins/workbench-resources/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import SpecialView from './components/SpecialView.svelte'
import WorkbenchApp from './components/WorkbenchApp.svelte'
import { doNavigate } from './utils'
import Workbench from './components/Workbench.svelte'
import ServerManager from './components/ServerManager.svelte'

async function hasArchiveSpaces (spaces: Space[]): Promise<boolean> {
return spaces.find((sp) => sp.archived) !== undefined
Expand All @@ -41,7 +42,8 @@ export default async (): Promise<Resources> => ({
SpacePanel,
SpecialView,
SpaceBrowser,
Workbench
Workbench,
ServerManager
},
function: {
HasArchiveSpaces: hasArchiveSpaces,
Expand Down
8 changes: 7 additions & 1 deletion server/ws/src/server_http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,18 @@ export function startHttpServer (
skipUTF8Validation: true
})
// eslint-disable-next-line @typescript-eslint/no-misused-promises
wss.on('connection', async (ws: WebSocket, request: any, token: Token, sessionId?: string) => {
wss.on('connection', async (ws: WebSocket, request: IncomingMessage, token: Token, sessionId?: string) => {
let buffer: Buffer[] | undefined = []

const data = {
remoteAddress: request.socket.remoteAddress ?? '',
userAgent: request.headers['user-agent'] ?? '',
language: request.headers['accept-language'] ?? ''
}
const cs: ConnectionSocket = {
id: generateId(),
close: () => ws.close(),
data: () => data,
send: async (ctx: MeasureContext, msg, binary, compression) => {
if (ws.readyState !== ws.OPEN) {
return
Expand Down
1 change: 1 addition & 0 deletions server/ws/src/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function getStatistics (ctx: MeasureContext, sessions: SessionManager, ad
for (const [k, v] of sessions.workspaces) {
data.statistics.activeSessions[k] = Array.from(v.sessions.entries()).map(([k, v]) => ({
userId: v.session.getUser(),
data: v.socket.data(),
mins5: v.session.mins5,
total: v.session.total,
current: v.session.current
Expand Down
1 change: 1 addition & 0 deletions server/ws/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface ConnectionSocket {
id: string
close: () => void
send: (ctx: MeasureContext, msg: Response<any>, binary: boolean, compression: boolean) => Promise<void>
data: () => Record<string, any>
}

/**
Expand Down