mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-25 18:02:04 +00:00
UBER-496: Fix few issues (#3439)
This commit is contained in:
parent
354e76e1f1
commit
05d273bb59
5
.vscode/launch.json
vendored
5
.vscode/launch.json
vendored
@ -69,7 +69,10 @@
|
|||||||
"TRANSACTOR_URL": "ws:/localhost:3333",
|
"TRANSACTOR_URL": "ws:/localhost:3333",
|
||||||
"ACCOUNT_PORT": "3000",
|
"ACCOUNT_PORT": "3000",
|
||||||
"FRONT_URL": "http://localhost:8080",
|
"FRONT_URL": "http://localhost:8080",
|
||||||
"SES_URL": "http://localhost:8091"
|
"SES_URL": "http://localhost:8091",
|
||||||
|
"MINIO_ACCESS_KEY": "minioadmin",
|
||||||
|
"MINIO_SECRET_KEY": "minioadmin",
|
||||||
|
"MINIO_ENDPOINT": "localhost"
|
||||||
},
|
},
|
||||||
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
|
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
|
@ -627,6 +627,9 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.mixin(core.class.Account, core.class.Class, view.mixin.ObjectPresenter, {
|
builder.mixin(core.class.Account, core.class.Class, view.mixin.ObjectPresenter, {
|
||||||
presenter: contact.component.EmployeeAccountPresenter
|
presenter: contact.component.EmployeeAccountPresenter
|
||||||
})
|
})
|
||||||
|
builder.mixin(core.class.Account, core.class.Class, view.mixin.AttributePresenter, {
|
||||||
|
presenter: contact.component.EmployeeAccountRefPresenter
|
||||||
|
})
|
||||||
|
|
||||||
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.ObjectPresenter, {
|
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.ObjectPresenter, {
|
||||||
presenter: contact.component.OrganizationPresenter
|
presenter: contact.component.OrganizationPresenter
|
||||||
|
@ -36,6 +36,7 @@ export default mergeIds(contactId, contact, {
|
|||||||
Contacts: '' as AnyComponent,
|
Contacts: '' as AnyComponent,
|
||||||
ContactsTabs: '' as AnyComponent,
|
ContactsTabs: '' as AnyComponent,
|
||||||
EmployeeAccountPresenter: '' as AnyComponent,
|
EmployeeAccountPresenter: '' as AnyComponent,
|
||||||
|
EmployeeAccountRefPresenter: '' as AnyComponent,
|
||||||
OrganizationEditor: '' as AnyComponent,
|
OrganizationEditor: '' as AnyComponent,
|
||||||
EmployeePresenter: '' as AnyComponent,
|
EmployeePresenter: '' as AnyComponent,
|
||||||
EmployeeRefPresenter: '' as AnyComponent,
|
EmployeeRefPresenter: '' as AnyComponent,
|
||||||
|
@ -651,7 +651,7 @@ export function createModel (builder: Builder): void {
|
|||||||
|
|
||||||
const applicantViewOptions = (colors: boolean): ViewOptionsModel => {
|
const applicantViewOptions = (colors: boolean): ViewOptionsModel => {
|
||||||
const model: ViewOptionsModel = {
|
const model: ViewOptionsModel = {
|
||||||
groupBy: ['state', 'assignee', 'space'],
|
groupBy: ['state', 'assignee', 'space', 'createdBy', 'modifiedBy'],
|
||||||
orderBy: [
|
orderBy: [
|
||||||
['state', SortingOrder.Ascending],
|
['state', SortingOrder.Ascending],
|
||||||
['modifiedOn', SortingOrder.Descending],
|
['modifiedOn', SortingOrder.Descending],
|
||||||
|
@ -70,7 +70,7 @@ import {
|
|||||||
TimeSpendReport,
|
TimeSpendReport,
|
||||||
trackerId
|
trackerId
|
||||||
} from '@hcengineering/tracker'
|
} from '@hcengineering/tracker'
|
||||||
import { KeyBinding, ViewOptionsModel } from '@hcengineering/view'
|
import { KeyBinding, ViewAction, ViewOptionsModel } from '@hcengineering/view'
|
||||||
import tracker from './plugin'
|
import tracker from './plugin'
|
||||||
|
|
||||||
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
|
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
|
||||||
@ -385,7 +385,7 @@ export function createModel (builder: Builder): void {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const issuesOptions = (kanban: boolean): ViewOptionsModel => ({
|
const issuesOptions = (kanban: boolean): ViewOptionsModel => ({
|
||||||
groupBy: ['status', 'assignee', 'priority', 'component', 'milestone'],
|
groupBy: ['status', 'assignee', 'priority', 'component', 'milestone', 'createdBy', 'modifiedBy'],
|
||||||
orderBy: [
|
orderBy: [
|
||||||
['status', SortingOrder.Ascending],
|
['status', SortingOrder.Ascending],
|
||||||
['priority', SortingOrder.Descending],
|
['priority', SortingOrder.Descending],
|
||||||
@ -554,7 +554,7 @@ export function createModel (builder: Builder): void {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const subIssuesOptions: ViewOptionsModel = {
|
const subIssuesOptions: ViewOptionsModel = {
|
||||||
groupBy: ['status', 'assignee', 'priority', 'milestone'],
|
groupBy: ['status', 'assignee', 'priority', 'milestone', 'createdBy', 'modifiedBy'],
|
||||||
orderBy: [
|
orderBy: [
|
||||||
['rank', SortingOrder.Ascending],
|
['rank', SortingOrder.Ascending],
|
||||||
['status', SortingOrder.Ascending],
|
['status', SortingOrder.Ascending],
|
||||||
@ -667,7 +667,7 @@ export function createModel (builder: Builder): void {
|
|||||||
attachTo: tracker.class.IssueTemplate,
|
attachTo: tracker.class.IssueTemplate,
|
||||||
descriptor: view.viewlet.List,
|
descriptor: view.viewlet.List,
|
||||||
viewOptions: {
|
viewOptions: {
|
||||||
groupBy: ['assignee', 'priority', 'component', 'milestone'],
|
groupBy: ['assignee', 'priority', 'component', 'milestone', 'createdBy', 'modifiedBy'],
|
||||||
orderBy: [
|
orderBy: [
|
||||||
['priority', SortingOrder.Ascending],
|
['priority', SortingOrder.Ascending],
|
||||||
['modifiedOn', SortingOrder.Descending],
|
['modifiedOn', SortingOrder.Descending],
|
||||||
@ -1060,6 +1060,20 @@ export function createModel (builder: Builder): void {
|
|||||||
['backlog', tracker.string.Backlog, {}]
|
['backlog', tracker.string.Backlog, {}]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'all-projects',
|
||||||
|
component: workbench.component.SpecialView,
|
||||||
|
icon: view.icon.Archive,
|
||||||
|
label: tracker.string.AllProjects,
|
||||||
|
position: 'bottom',
|
||||||
|
visibleIf: workbench.function.IsOwner,
|
||||||
|
spaceClass: tracker.class.Project,
|
||||||
|
componentProps: {
|
||||||
|
_class: tracker.class.Project,
|
||||||
|
label: tracker.string.AllIssues,
|
||||||
|
icon: tracker.icon.Issues
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
spaces: [
|
spaces: [
|
||||||
@ -1068,6 +1082,7 @@ export function createModel (builder: Builder): void {
|
|||||||
spaceClass: tracker.class.Project,
|
spaceClass: tracker.class.Project,
|
||||||
addSpaceLabel: tracker.string.CreateProject,
|
addSpaceLabel: tracker.string.CreateProject,
|
||||||
createComponent: tracker.component.CreateProject,
|
createComponent: tracker.component.CreateProject,
|
||||||
|
visibleIf: tracker.function.IsProjectJoined,
|
||||||
icon: tracker.icon.Home,
|
icon: tracker.icon.Home,
|
||||||
specials: [
|
specials: [
|
||||||
{
|
{
|
||||||
@ -1162,9 +1177,7 @@ export function createModel (builder: Builder): void {
|
|||||||
input: 'focus',
|
input: 'focus',
|
||||||
category: tracker.category.Tracker,
|
category: tracker.category.Tracker,
|
||||||
target: tracker.class.Project,
|
target: tracker.class.Project,
|
||||||
query: {
|
query: {},
|
||||||
archived: false
|
|
||||||
},
|
|
||||||
context: {
|
context: {
|
||||||
mode: ['context', 'browser'],
|
mode: ['context', 'browser'],
|
||||||
group: 'edit'
|
group: 'edit'
|
||||||
@ -1182,9 +1195,7 @@ export function createModel (builder: Builder): void {
|
|||||||
input: 'focus',
|
input: 'focus',
|
||||||
category: tracker.category.Tracker,
|
category: tracker.category.Tracker,
|
||||||
target: tracker.class.Project,
|
target: tracker.class.Project,
|
||||||
query: {
|
query: {},
|
||||||
archived: false
|
|
||||||
},
|
|
||||||
context: {
|
context: {
|
||||||
mode: ['context', 'browser'],
|
mode: ['context', 'browser'],
|
||||||
group: 'edit'
|
group: 'edit'
|
||||||
@ -1213,6 +1224,28 @@ export function createModel (builder: Builder): void {
|
|||||||
},
|
},
|
||||||
tracker.action.DeleteProject
|
tracker.action.DeleteProject
|
||||||
)
|
)
|
||||||
|
createAction(builder, {
|
||||||
|
label: tracker.string.Unarchive,
|
||||||
|
icon: view.icon.Archive,
|
||||||
|
action: view.actionImpl.UpdateDocument as ViewAction,
|
||||||
|
actionProps: {
|
||||||
|
key: 'archived',
|
||||||
|
ask: true,
|
||||||
|
value: false,
|
||||||
|
label: tracker.string.Unarchive,
|
||||||
|
message: tracker.string.UnarchiveConfirm
|
||||||
|
},
|
||||||
|
input: 'any',
|
||||||
|
category: tracker.category.Tracker,
|
||||||
|
query: {
|
||||||
|
archived: true
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
mode: ['context', 'browser'],
|
||||||
|
group: 'tools'
|
||||||
|
},
|
||||||
|
target: tracker.class.Project
|
||||||
|
})
|
||||||
|
|
||||||
createAction(
|
createAction(
|
||||||
builder,
|
builder,
|
||||||
@ -1391,6 +1424,10 @@ export function createModel (builder: Builder): void {
|
|||||||
override: [view.action.Open]
|
override: [view.action.Open]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(tracker.class.Project, core.class.Class, view.mixin.IgnoreActions, {
|
||||||
|
actions: [view.action.Open]
|
||||||
|
})
|
||||||
|
|
||||||
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ClassFilters, {
|
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ClassFilters, {
|
||||||
filters: [
|
filters: [
|
||||||
'status',
|
'status',
|
||||||
@ -1852,7 +1889,7 @@ export function createModel (builder: Builder): void {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const milestoneOptions: ViewOptionsModel = {
|
const milestoneOptions: ViewOptionsModel = {
|
||||||
groupBy: ['status'],
|
groupBy: ['status', 'createdBy', 'modifiedBy'],
|
||||||
orderBy: [
|
orderBy: [
|
||||||
['modifiedOn', SortingOrder.Descending],
|
['modifiedOn', SortingOrder.Descending],
|
||||||
['targetDate', SortingOrder.Descending],
|
['targetDate', SortingOrder.Descending],
|
||||||
@ -1934,7 +1971,7 @@ export function createModel (builder: Builder): void {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const componentListViewOptions: ViewOptionsModel = {
|
const componentListViewOptions: ViewOptionsModel = {
|
||||||
groupBy: ['lead'],
|
groupBy: ['lead', 'createdBy', 'modifiedBy'],
|
||||||
orderBy: [
|
orderBy: [
|
||||||
['modifiedOn', SortingOrder.Descending],
|
['modifiedOn', SortingOrder.Descending],
|
||||||
['createdOn', SortingOrder.Descending]
|
['createdOn', SortingOrder.Descending]
|
||||||
@ -1973,4 +2010,33 @@ export function createModel (builder: Builder): void {
|
|||||||
},
|
},
|
||||||
tracker.viewlet.ComponentList
|
tracker.viewlet.ComponentList
|
||||||
)
|
)
|
||||||
|
|
||||||
|
builder.createDoc(
|
||||||
|
view.class.Viewlet,
|
||||||
|
core.space.Model,
|
||||||
|
{
|
||||||
|
attachTo: tracker.class.Project,
|
||||||
|
descriptor: view.viewlet.List,
|
||||||
|
viewOptions: {
|
||||||
|
groupBy: ['createdBy'],
|
||||||
|
orderBy: [
|
||||||
|
['modifiedOn', SortingOrder.Descending],
|
||||||
|
['createdOn', SortingOrder.Descending]
|
||||||
|
],
|
||||||
|
other: [showColorsViewOption]
|
||||||
|
},
|
||||||
|
configOptions: {
|
||||||
|
strict: true,
|
||||||
|
hiddenKeys: ['label', 'description']
|
||||||
|
},
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
key: '',
|
||||||
|
props: { kind: 'list' }
|
||||||
|
},
|
||||||
|
{ key: '', displayProps: { grow: true } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
tracker.viewlet.ProjectList
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,10 @@ export default mergeIds(trackerId, tracker, {
|
|||||||
CreatedDate: '' as IntlString,
|
CreatedDate: '' as IntlString,
|
||||||
ChangeStatus: '' as IntlString,
|
ChangeStatus: '' as IntlString,
|
||||||
ConfigLabel: '' as IntlString,
|
ConfigLabel: '' as IntlString,
|
||||||
ConfigDescription: '' as IntlString
|
ConfigDescription: '' as IntlString,
|
||||||
|
Unarchive: '' as IntlString,
|
||||||
|
UnarchiveConfirm: '' as IntlString,
|
||||||
|
AllProjects: '' as IntlString
|
||||||
},
|
},
|
||||||
activity: {
|
activity: {
|
||||||
TxIssueCreated: '' as AnyComponent,
|
TxIssueCreated: '' as AnyComponent,
|
||||||
@ -62,7 +65,8 @@ export default mergeIds(trackerId, tracker, {
|
|||||||
IssueTemplateList: '' as Ref<Viewlet>,
|
IssueTemplateList: '' as Ref<Viewlet>,
|
||||||
IssueKanban: '' as Ref<Viewlet>,
|
IssueKanban: '' as Ref<Viewlet>,
|
||||||
MilestoneList: '' as Ref<Viewlet>,
|
MilestoneList: '' as Ref<Viewlet>,
|
||||||
ComponentList: '' as Ref<Viewlet>
|
ComponentList: '' as Ref<Viewlet>,
|
||||||
|
ProjectList: '' as Ref<Viewlet>
|
||||||
},
|
},
|
||||||
ids: {
|
ids: {
|
||||||
TxIssueCreated: '' as Ref<TxViewlet>,
|
TxIssueCreated: '' as Ref<TxViewlet>,
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Space } from '@hcengineering/core'
|
import { Doc, Space } from '@hcengineering/core'
|
||||||
import { IntlString, mergeIds, Resource } from '@hcengineering/platform'
|
import { IntlString, Resource, mergeIds } from '@hcengineering/platform'
|
||||||
import { AnyComponent } from '@hcengineering/ui'
|
import { AnyComponent } from '@hcengineering/ui'
|
||||||
import { workbenchId } from '@hcengineering/workbench'
|
import { workbenchId } from '@hcengineering/workbench'
|
||||||
import workbench from '@hcengineering/workbench-resources/src/plugin'
|
import workbench from '@hcengineering/workbench-resources/src/plugin'
|
||||||
@ -32,6 +32,7 @@ export default mergeIds(workbenchId, workbench, {
|
|||||||
HiddenApplication: '' as IntlString
|
HiddenApplication: '' as IntlString
|
||||||
},
|
},
|
||||||
function: {
|
function: {
|
||||||
HasArchiveSpaces: '' as Resource<(spaces: Space[]) => Promise<boolean>>
|
HasArchiveSpaces: '' as Resource<(spaces: Space[]) => Promise<boolean>>,
|
||||||
|
IsOwner: '' as Resource<(docs: Doc[]) => Promise<boolean>>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -7,6 +7,18 @@
|
|||||||
"heftEvent": "clean",
|
"heftEvent": "clean",
|
||||||
"actionId": "defaultClean",
|
"actionId": "defaultClean",
|
||||||
"globsToDelete": ["dist", "lib", "temp"]
|
"globsToDelete": ["dist", "lib", "temp"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actionKind": "copyFiles",
|
||||||
|
"heftEvent": "pre-compile",
|
||||||
|
"actionId": "copy-lang",
|
||||||
|
"copyOperations": [
|
||||||
|
{
|
||||||
|
"sourceFolder": "src",
|
||||||
|
"destinationFolders": ["lib"],
|
||||||
|
"includeGlobs": ["lang"]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"heftPlugins": [
|
"heftPlugins": [
|
||||||
|
@ -7,6 +7,18 @@
|
|||||||
"heftEvent": "clean",
|
"heftEvent": "clean",
|
||||||
"actionId": "defaultClean",
|
"actionId": "defaultClean",
|
||||||
"globsToDelete": ["dist", "lib", "temp"]
|
"globsToDelete": ["dist", "lib", "temp"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actionKind": "copyFiles",
|
||||||
|
"heftEvent": "pre-compile",
|
||||||
|
"actionId": "copy-lang",
|
||||||
|
"copyOperations": [
|
||||||
|
{
|
||||||
|
"sourceFolder": "src",
|
||||||
|
"destinationFolders": ["lib"],
|
||||||
|
"includeGlobs": ["lang"]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"heftPlugins": [
|
"heftPlugins": [
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"WorkspaceNotFound": "Workspace not found",
|
"WorkspaceNotFound": "Workspace not found",
|
||||||
"InvalidPassword": "Invalid password",
|
"InvalidPassword": "Invalid password",
|
||||||
"AccountAlreadyExists": "Account already exists",
|
"AccountAlreadyExists": "Account already exists",
|
||||||
|
"WorkspaceRateLimit": "Server is busy, Please wait a bit and try again",
|
||||||
"AccountAlreadyConfirmed": "Account already confirmed",
|
"AccountAlreadyConfirmed": "Account already confirmed",
|
||||||
"AccountWasMerged": "Account was merged",
|
"AccountWasMerged": "Account was merged",
|
||||||
"WorkspaceAlreadyExists": "Workspace already exists",
|
"WorkspaceAlreadyExists": "Workspace already exists",
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"WorkspaceNotFound": "Рабочее пространство не найдено",
|
"WorkspaceNotFound": "Рабочее пространство не найдено",
|
||||||
"InvalidPassword": "Неверный пароль",
|
"InvalidPassword": "Неверный пароль",
|
||||||
"AccountAlreadyExists": "Аккаунт уже существует",
|
"AccountAlreadyExists": "Аккаунт уже существует",
|
||||||
|
"WorkspaceRateLimit": "Сервер перегружен, Пожалуйста подождите",
|
||||||
"AccountAlreadyConfirmed": "Аккаунт уже подтвержден",
|
"AccountAlreadyConfirmed": "Аккаунт уже подтвержден",
|
||||||
"AccountWasMerged": "Аккаунт был объединен",
|
"AccountWasMerged": "Аккаунт был объединен",
|
||||||
"WorkspaceAlreadyExists": "Рабочее пространство уже существует",
|
"WorkspaceAlreadyExists": "Рабочее пространство уже существует",
|
||||||
|
@ -152,6 +152,7 @@ export default plugin(platformId, {
|
|||||||
AccountAlreadyConfirmed: '' as StatusCode<{ account: string }>,
|
AccountAlreadyConfirmed: '' as StatusCode<{ account: string }>,
|
||||||
AccountWasMerged: '' as StatusCode<{ account: string }>,
|
AccountWasMerged: '' as StatusCode<{ account: string }>,
|
||||||
WorkspaceAlreadyExists: '' as StatusCode<{ workspace: string }>,
|
WorkspaceAlreadyExists: '' as StatusCode<{ workspace: string }>,
|
||||||
|
WorkspaceRateLimit: '' as StatusCode<{ workspace: string }>,
|
||||||
ProductIdMismatch: '' as StatusCode<{ productId: string }>
|
ProductIdMismatch: '' as StatusCode<{ productId: string }>
|
||||||
},
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
{#if value}
|
{#if value}
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
{#if employee}
|
{#if employee}
|
||||||
<EmployeePresenter value={employee} {disabled} {inline} {accent} {avatarSize} />
|
<EmployeePresenter value={employee} {disabled} {inline} {accent} {avatarSize} on:accent-color />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex-row-center">
|
<div class="flex-row-center">
|
||||||
<Avatar size={avatarSize} />
|
<Avatar size={avatarSize} />
|
||||||
|
@ -30,5 +30,5 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if account}
|
{#if account}
|
||||||
<EmployeeAccountPresenter value={account} {disabled} {inline} {avatarSize} />
|
<EmployeeAccountPresenter value={account} {disabled} {inline} {avatarSize} on:accent-color />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -68,6 +68,7 @@ import EmployeePresenter from './components/EmployeePresenter.svelte'
|
|||||||
import EmployeeRefPresenter from './components/EmployeeRefPresenter.svelte'
|
import EmployeeRefPresenter from './components/EmployeeRefPresenter.svelte'
|
||||||
import MemberPresenter from './components/MemberPresenter.svelte'
|
import MemberPresenter from './components/MemberPresenter.svelte'
|
||||||
import Members from './components/Members.svelte'
|
import Members from './components/Members.svelte'
|
||||||
|
import MembersBox from './components/MembersBox.svelte'
|
||||||
import MembersPresenter from './components/MembersPresenter.svelte'
|
import MembersPresenter from './components/MembersPresenter.svelte'
|
||||||
import MergeEmployee from './components/MergeEmployee.svelte'
|
import MergeEmployee from './components/MergeEmployee.svelte'
|
||||||
import OrganizationEditor from './components/OrganizationEditor.svelte'
|
import OrganizationEditor from './components/OrganizationEditor.svelte'
|
||||||
@ -141,7 +142,8 @@ export {
|
|||||||
UserInfo,
|
UserInfo,
|
||||||
IconMembers,
|
IconMembers,
|
||||||
SelectAvatars,
|
SelectAvatars,
|
||||||
UserBoxItems
|
UserBoxItems,
|
||||||
|
MembersBox
|
||||||
}
|
}
|
||||||
|
|
||||||
const toObjectSearchResult = (e: WithLookup<Contact>): ObjectSearchResult => ({
|
const toObjectSearchResult = (e: WithLookup<Contact>): ObjectSearchResult => ({
|
||||||
@ -311,7 +313,8 @@ export default async (): Promise<Resources> => ({
|
|||||||
EmployeeFilter,
|
EmployeeFilter,
|
||||||
EmployeeFilterValuePresenter,
|
EmployeeFilterValuePresenter,
|
||||||
EmployeeAccountFilterValuePresenter,
|
EmployeeAccountFilterValuePresenter,
|
||||||
DeleteConfirmationPopup
|
DeleteConfirmationPopup,
|
||||||
|
EmployeeAccountRefPresenter
|
||||||
},
|
},
|
||||||
completion: {
|
completion: {
|
||||||
EmployeeQuery: async (
|
EmployeeQuery: async (
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
import type { Ref } from '@hcengineering/core'
|
import type { Ref } from '@hcengineering/core'
|
||||||
import core from '@hcengineering/core'
|
import core from '@hcengineering/core'
|
||||||
import { Panel } from '@hcengineering/panel'
|
import { Panel } from '@hcengineering/panel'
|
||||||
import { createQuery, getClient, MembersBox } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
|
import { MembersBox } from '@hcengineering/contact-resources'
|
||||||
import type { Funnel } from '@hcengineering/lead'
|
import type { Funnel } from '@hcengineering/lead'
|
||||||
import { FullDescriptionBox } from '@hcengineering/text-editor'
|
import { FullDescriptionBox } from '@hcengineering/text-editor'
|
||||||
import { EditBox, Grid } from '@hcengineering/ui'
|
import { EditBox, Grid } from '@hcengineering/ui'
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
$: queries = { assigned, created, subscribed }
|
$: queries = { assigned, created, subscribed }
|
||||||
$: mode = $resolvedLocationStore.query?.mode ?? undefined
|
$: mode = $resolvedLocationStore.query?.mode ?? undefined
|
||||||
|
|
||||||
let searchQuery: DocumentQuery<Lead> = { ...baseQuery }
|
let searchQuery: DocumentQuery<Lead> = { ...(baseQuery ?? {}) }
|
||||||
function updateSearchQuery (search: string): void {
|
function updateSearchQuery (search: string): void {
|
||||||
searchQuery = search === '' ? { ...baseQuery } : { ...baseQuery, $search: search }
|
searchQuery = search === '' ? { ...baseQuery } : { ...baseQuery, $search: search }
|
||||||
}
|
}
|
||||||
@ -72,11 +72,11 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
$: if (mode === 'subscribed') getSubscribed()
|
$: if (mode === 'subscribed') getSubscribed()
|
||||||
$: if (mode === undefined || queries[mode] === undefined) {
|
$: if (mode === undefined || (queries as any)[mode] === undefined) {
|
||||||
;[[mode]] = config
|
;[[mode]] = config
|
||||||
}
|
}
|
||||||
$: if (mode !== undefined) {
|
$: if (mode !== undefined) {
|
||||||
baseQuery = { ...queries[mode] }
|
baseQuery = { ...((queries as any)[mode] ?? {}) }
|
||||||
modeSelectorProps = {
|
modeSelectorProps = {
|
||||||
config,
|
config,
|
||||||
mode,
|
mode,
|
||||||
@ -101,6 +101,9 @@
|
|||||||
.findOne<Viewlet>(view.class.Viewlet, { attachTo: _class, descriptor: task.viewlet.StatusTable })
|
.findOne<Viewlet>(view.class.Viewlet, { attachTo: _class, descriptor: task.viewlet.StatusTable })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
viewlet = res
|
viewlet = res
|
||||||
|
if (res == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
preferenceQuery.query(
|
preferenceQuery.query(
|
||||||
view.class.ViewletPreference,
|
view.class.ViewletPreference,
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"WantAnotherWorkspace": "Want to create another workspace?",
|
"WantAnotherWorkspace": "Want to create another workspace?",
|
||||||
"ChangeAccount": "Change account",
|
"ChangeAccount": "Change account",
|
||||||
"NotSeeingWorkspace": "Not seeing your workspace?",
|
"NotSeeingWorkspace": "Not seeing your workspace?",
|
||||||
"WorkspaceNameRule": "The workspace name can contain lowercase letters, numbers and symbol -",
|
"WorkspaceNameRule": "The workspace name can contain lowercase letters, numbers and symbol - (inside name)",
|
||||||
"ForgotPassword": "Forgot your password?",
|
"ForgotPassword": "Forgot your password?",
|
||||||
"KnowPassword": "Know your password?",
|
"KnowPassword": "Know your password?",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"WantAnotherWorkspace": "Хотите создать другое рабочее пространство?",
|
"WantAnotherWorkspace": "Хотите создать другое рабочее пространство?",
|
||||||
"ChangeAccount": "Сменить пользователя",
|
"ChangeAccount": "Сменить пользователя",
|
||||||
"NotSeeingWorkspace": "Не видите ваше рабочее пространство?",
|
"NotSeeingWorkspace": "Не видите ваше рабочее пространство?",
|
||||||
"WorkspaceNameRule": "Название рабочего пространства должно состояить из строчных латинских букв, цифр и символа -",
|
"WorkspaceNameRule": "Название рабочего пространства должно состояить из строчных латинских букв, цифр и символа - (внутри имени)",
|
||||||
"ForgotPassword": "Забыли пароль?",
|
"ForgotPassword": "Забыли пароль?",
|
||||||
"KnowPassword": "Знаете пароль?",
|
"KnowPassword": "Знаете пароль?",
|
||||||
"Recover": "Восстановить",
|
"Recover": "Восстановить",
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
{
|
{
|
||||||
name: 'workspace',
|
name: 'workspace',
|
||||||
i18n: login.string.Workspace,
|
i18n: login.string.Workspace,
|
||||||
rule: /^[0-9a-z\-)(]{3,63}$/,
|
rule: /^[0-9a-z][0-9a-z-]{2,62}[0-9a-z]$/,
|
||||||
ruleDescr: login.string.WorkspaceNameRule
|
ruleDescr: login.string.WorkspaceNameRule
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
||||||
import chunter, { Comment } from '@hcengineering/chunter'
|
import chunter, { Comment } from '@hcengineering/chunter'
|
||||||
import { updateBacklinks } from '@hcengineering/chunter-resources/src/backlinks'
|
import { updateBacklinks } from '@hcengineering/chunter-resources'
|
||||||
import { EmployeeAccount } from '@hcengineering/contact'
|
import { EmployeeAccount } from '@hcengineering/contact'
|
||||||
import { AttachedData, getCurrentAccount, Ref } from '@hcengineering/core'
|
import { AttachedData, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
|
@ -273,7 +273,10 @@
|
|||||||
|
|
||||||
"NoStatusFound": "No matching status found",
|
"NoStatusFound": "No matching status found",
|
||||||
"CreateMissingStatus": "Create missing status",
|
"CreateMissingStatus": "Create missing status",
|
||||||
"UnsetParent": "Parent issue will be unset"
|
"UnsetParent": "Parent issue will be unset",
|
||||||
|
"Unarchive": "Unarchive",
|
||||||
|
"UnarchiveConfirm": "Do you want to unarchive project?",
|
||||||
|
"AllProjects": "All projects"
|
||||||
},
|
},
|
||||||
"status": {}
|
"status": {}
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,10 @@
|
|||||||
"ProjectEmojiiCategory": "Эмодзи",
|
"ProjectEmojiiCategory": "Эмодзи",
|
||||||
"NoStatusFound": "Статус не найдет",
|
"NoStatusFound": "Статус не найдет",
|
||||||
"CreateMissingStatus": "Создать отсутствущий статус",
|
"CreateMissingStatus": "Создать отсутствущий статус",
|
||||||
"UnsetParent": "Родительская задача будет убрана"
|
"UnsetParent": "Родительская задача будет убрана",
|
||||||
|
"Unarchive": "Разархивировать",
|
||||||
|
"UnarchiveConfirm": "Вы действительно хотите разархивировать?",
|
||||||
|
"AllProjects": "All projects"
|
||||||
},
|
},
|
||||||
"status": {}
|
"status": {}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,17 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Class, Client, Doc, DocumentQuery, Ref, RelatedDocument, toIdMap, TxOperations } from '@hcengineering/core'
|
import {
|
||||||
|
Class,
|
||||||
|
Client,
|
||||||
|
Doc,
|
||||||
|
DocumentQuery,
|
||||||
|
getCurrentAccount,
|
||||||
|
Ref,
|
||||||
|
RelatedDocument,
|
||||||
|
toIdMap,
|
||||||
|
TxOperations
|
||||||
|
} from '@hcengineering/core'
|
||||||
import { Resources, translate } from '@hcengineering/platform'
|
import { Resources, translate } from '@hcengineering/platform'
|
||||||
import { getClient, MessageBox, ObjectSearchResult } from '@hcengineering/presentation'
|
import { getClient, MessageBox, ObjectSearchResult } from '@hcengineering/presentation'
|
||||||
import { Issue, Milestone, Project } from '@hcengineering/tracker'
|
import { Issue, Milestone, Project } from '@hcengineering/tracker'
|
||||||
@ -419,7 +429,8 @@ export default async (): Promise<Resources> => ({
|
|||||||
GetAllPriority: getAllPriority,
|
GetAllPriority: getAllPriority,
|
||||||
GetAllComponents: getAllComponents,
|
GetAllComponents: getAllComponents,
|
||||||
GetAllMilestones: getAllMilestones,
|
GetAllMilestones: getAllMilestones,
|
||||||
GetVisibleFilters: getVisibleFilters
|
GetVisibleFilters: getVisibleFilters,
|
||||||
|
IsProjectJoined: async (project: Project) => !project.private || project.members.includes(getCurrentAccount()._id)
|
||||||
},
|
},
|
||||||
actionImpl: {
|
actionImpl: {
|
||||||
Move: move,
|
Move: move,
|
||||||
|
@ -21,11 +21,11 @@ import {
|
|||||||
CreateAggregationManagerFunc,
|
CreateAggregationManagerFunc,
|
||||||
GetAllValuesFunc,
|
GetAllValuesFunc,
|
||||||
GrouppingManagerResource,
|
GrouppingManagerResource,
|
||||||
|
KeyFilter,
|
||||||
SortFunc,
|
SortFunc,
|
||||||
Viewlet,
|
|
||||||
ViewletDescriptor,
|
|
||||||
ViewQueryAction,
|
ViewQueryAction,
|
||||||
KeyFilter
|
Viewlet,
|
||||||
|
ViewletDescriptor
|
||||||
} from '@hcengineering/view'
|
} from '@hcengineering/view'
|
||||||
import tracker, { trackerId } from '../../tracker/lib'
|
import tracker, { trackerId } from '../../tracker/lib'
|
||||||
|
|
||||||
@ -382,7 +382,8 @@ export default mergeIds(trackerId, tracker, {
|
|||||||
GetAllPriority: '' as GetAllValuesFunc,
|
GetAllPriority: '' as GetAllValuesFunc,
|
||||||
GetAllComponents: '' as GetAllValuesFunc,
|
GetAllComponents: '' as GetAllValuesFunc,
|
||||||
GetAllMilestones: '' as GetAllValuesFunc,
|
GetAllMilestones: '' as GetAllValuesFunc,
|
||||||
GetVisibleFilters: '' as Resource<(filters: KeyFilter[], space?: Ref<Space>) => Promise<KeyFilter[]>>
|
GetVisibleFilters: '' as Resource<(filters: KeyFilter[], space?: Ref<Space>) => Promise<KeyFilter[]>>,
|
||||||
|
IsProjectJoined: '' as Resource<(space: Space) => Promise<boolean>>
|
||||||
},
|
},
|
||||||
aggregation: {
|
aggregation: {
|
||||||
CreateComponentAggregationManager: '' as CreateAggregationManagerFunc,
|
CreateComponentAggregationManager: '' as CreateAggregationManagerFunc,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Class, Doc, Ref, Space, WithLookup } from '@hcengineering/core'
|
import { Class, Doc, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||||
import { Asset, IntlString } from '@hcengineering/platform'
|
import { Asset, IntlString } from '@hcengineering/platform'
|
||||||
import { createQuery } from '@hcengineering/presentation'
|
import { createQuery } from '@hcengineering/presentation'
|
||||||
import {
|
import {
|
||||||
@ -50,6 +50,7 @@
|
|||||||
export let createComponentProps: Record<string, any> = {}
|
export let createComponentProps: Record<string, any> = {}
|
||||||
export let isCreationDisabled = false
|
export let isCreationDisabled = false
|
||||||
export let descriptors: Ref<ViewletDescriptor>[] | undefined = undefined
|
export let descriptors: Ref<ViewletDescriptor>[] | undefined = undefined
|
||||||
|
export let baseQuery: DocumentQuery<Doc> | undefined = undefined
|
||||||
|
|
||||||
let search = ''
|
let search = ''
|
||||||
let viewlet: WithLookup<Viewlet> | undefined
|
let viewlet: WithLookup<Viewlet> | undefined
|
||||||
@ -116,7 +117,7 @@
|
|||||||
preferenceQuery.unsubscribe()
|
preferenceQuery.unsubscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
$: query = viewlet?.baseQuery ?? {}
|
$: query = { ...(baseQuery ?? {}), ...(viewlet?.baseQuery ?? {}) }
|
||||||
$: searchQuery = search === '' ? query : { ...query, $search: search }
|
$: searchQuery = search === '' ? query : { ...query, $search: search }
|
||||||
$: resultQuery = searchQuery
|
$: resultQuery = searchQuery
|
||||||
|
|
||||||
@ -186,7 +187,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if !viewlet?.$lookup?.descriptor?.component || viewlet?.attachTo !== _class || (preference !== undefined && viewlet?._id !== preference.attachedTo)}}
|
{#if !viewlet?.$lookup?.descriptor?.component || viewlet?.attachTo !== _class || (preference !== undefined && viewlet?._id !== preference.attachedTo)}
|
||||||
<Loading />
|
<Loading />
|
||||||
{:else}
|
{:else}
|
||||||
<FilterBar
|
<FilterBar
|
||||||
|
@ -117,6 +117,32 @@
|
|||||||
return await getResource(value.presenter)
|
return await getResource(value.presenter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let visibleIf: ((space: Space) => Promise<boolean>) | undefined
|
||||||
|
|
||||||
|
$: if (model.visibleIf) {
|
||||||
|
getResource(model.visibleIf).then((r) => {
|
||||||
|
visibleIf = r
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let filteredSpaces: Space[] = []
|
||||||
|
|
||||||
|
async function updateSpaces (spaces: Space[], visibleIf: (space: Space) => Promise<boolean>): Promise<void> {
|
||||||
|
const result: Space[] = []
|
||||||
|
for (const s of spaces) {
|
||||||
|
if (await visibleIf(s)) {
|
||||||
|
result.push(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filteredSpaces = result
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (visibleIf) {
|
||||||
|
updateSpaces(spaces, visibleIf)
|
||||||
|
} else {
|
||||||
|
filteredSpaces = spaces
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<TreeNode
|
<TreeNode
|
||||||
@ -125,7 +151,7 @@
|
|||||||
actions={async () => getParentActions()}
|
actions={async () => getParentActions()}
|
||||||
shortDropbox={model.specials !== undefined}
|
shortDropbox={model.specials !== undefined}
|
||||||
>
|
>
|
||||||
{#each spaces as space, i (space._id)}
|
{#each filteredSpaces as space, i (space._id)}
|
||||||
{#await getPresenter(space._class) then presenter}
|
{#await getPresenter(space._class) then presenter}
|
||||||
{#if separate && model.specials && i !== 0}<TreeSeparator line />{/if}
|
{#if separate && model.specials && i !== 0}<TreeSeparator line />{/if}
|
||||||
{#if model.specials && presenter}
|
{#if model.specials && presenter}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Space } from '@hcengineering/core'
|
import { AccountRole, Space, getCurrentAccount } from '@hcengineering/core'
|
||||||
import { Resources } from '@hcengineering/platform'
|
import { Resources } from '@hcengineering/platform'
|
||||||
import ApplicationPresenter from './components/ApplicationPresenter.svelte'
|
import ApplicationPresenter from './components/ApplicationPresenter.svelte'
|
||||||
import Archive from './components/Archive.svelte'
|
import Archive from './components/Archive.svelte'
|
||||||
@ -42,7 +42,8 @@ export default async (): Promise<Resources> => ({
|
|||||||
Workbench
|
Workbench
|
||||||
},
|
},
|
||||||
function: {
|
function: {
|
||||||
HasArchiveSpaces: hasArchiveSpaces
|
HasArchiveSpaces: hasArchiveSpaces,
|
||||||
|
IsOwner: async (docs: Space[]) => getCurrentAccount().role === AccountRole.Owner
|
||||||
},
|
},
|
||||||
actionImpl: {
|
actionImpl: {
|
||||||
Navigate: doNavigate
|
Navigate: doNavigate
|
||||||
|
@ -59,6 +59,8 @@ export interface SpacesNavModel {
|
|||||||
|
|
||||||
// Child special items.
|
// Child special items.
|
||||||
specials?: SpecialNavModel[]
|
specials?: SpecialNavModel[]
|
||||||
|
|
||||||
|
visibleIf?: Resource<(space: Space) => Promise<boolean>>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,7 +97,6 @@ export function serveAccount (methods: Record<string, AccountMethod>, productId
|
|||||||
}
|
}
|
||||||
const db = client.db(ACCOUNT_DB)
|
const db = client.db(ACCOUNT_DB)
|
||||||
const result = await method(db, productId, request, token)
|
const result = await method(db, productId, request, token)
|
||||||
console.log(result)
|
|
||||||
ctx.body = result
|
ctx.body = result
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -104,6 +104,7 @@ export interface Account {
|
|||||||
// Defined for server admins only
|
// Defined for server admins only
|
||||||
admin?: boolean
|
admin?: boolean
|
||||||
confirmed?: boolean
|
confirmed?: boolean
|
||||||
|
lastWorkspace?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,6 +116,7 @@ export interface Workspace {
|
|||||||
organisation: string
|
organisation: string
|
||||||
accounts: ObjectId[]
|
accounts: ObjectId[]
|
||||||
productId: string
|
productId: string
|
||||||
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -285,6 +287,9 @@ export async function selectWorkspace (
|
|||||||
const workspaceInfo = await getWorkspace(db, productId, workspace)
|
const workspaceInfo = await getWorkspace(db, productId, workspace)
|
||||||
|
|
||||||
if (workspaceInfo !== null) {
|
if (workspaceInfo !== null) {
|
||||||
|
if (workspaceInfo.disabled === true) {
|
||||||
|
throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace }))
|
||||||
|
}
|
||||||
const workspaces = accountInfo.workspaces
|
const workspaces = accountInfo.workspaces
|
||||||
|
|
||||||
for (const w of workspaces) {
|
for (const w of workspaces) {
|
||||||
@ -347,6 +352,7 @@ export async function join (
|
|||||||
): Promise<WorkspaceLoginInfo> {
|
): Promise<WorkspaceLoginInfo> {
|
||||||
const invite = await getInvite(db, inviteId)
|
const invite = await getInvite(db, inviteId)
|
||||||
const workspace = await checkInvite(invite, email)
|
const workspace = await checkInvite(invite, email)
|
||||||
|
console.log(`join attempt:${email}, ${workspace.name}`)
|
||||||
await assignWorkspace(db, productId, email, workspace.name)
|
await assignWorkspace(db, productId, email, workspace.name)
|
||||||
|
|
||||||
const token = (await login(db, productId, email, password)).token
|
const token = (await login(db, productId, email, password)).token
|
||||||
@ -360,6 +366,7 @@ export async function join (
|
|||||||
*/
|
*/
|
||||||
export async function confirmEmail (db: Db, email: string): Promise<Account> {
|
export async function confirmEmail (db: Db, email: string): Promise<Account> {
|
||||||
const account = await getAccount(db, email)
|
const account = await getAccount(db, email)
|
||||||
|
console.log(`confirm email:${email}`)
|
||||||
|
|
||||||
if (account === null) {
|
if (account === null) {
|
||||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: accountId }))
|
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: accountId }))
|
||||||
@ -446,6 +453,7 @@ export async function signUpJoin (
|
|||||||
last: string,
|
last: string,
|
||||||
inviteId: ObjectId
|
inviteId: ObjectId
|
||||||
): Promise<WorkspaceLoginInfo> {
|
): Promise<WorkspaceLoginInfo> {
|
||||||
|
console.log(`signup join:${email} ${first} ${last}`)
|
||||||
const invite = await getInvite(db, inviteId)
|
const invite = await getInvite(db, inviteId)
|
||||||
const workspace = await checkInvite(invite, email)
|
const workspace = await checkInvite(invite, email)
|
||||||
await createAcc(db, productId, email, password, first, last, invite?.emailMask === email)
|
await createAcc(db, productId, email, password, first, last, invite?.emailMask === email)
|
||||||
@ -529,6 +537,7 @@ export async function createAccount (
|
|||||||
export async function listWorkspaces (db: Db, productId: string): Promise<WorkspaceInfoOnly[]> {
|
export async function listWorkspaces (db: Db, productId: string): Promise<WorkspaceInfoOnly[]> {
|
||||||
return (await db.collection<Workspace>(WORKSPACE_COLLECTION).find(withProductId(productId, {})).toArray())
|
return (await db.collection<Workspace>(WORKSPACE_COLLECTION).find(withProductId(productId, {})).toArray())
|
||||||
.map((it) => ({ ...it, productId }))
|
.map((it) => ({ ...it, productId }))
|
||||||
|
.filter((it) => it.disabled !== true)
|
||||||
.map(trimWorkspace)
|
.map(trimWorkspace)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,20 +621,59 @@ export async function upgradeWorkspace (
|
|||||||
export const createUserWorkspace =
|
export const createUserWorkspace =
|
||||||
(version: Data<Version>, txes: Tx[], migrationOperation: [string, MigrateOperation][]) =>
|
(version: Data<Version>, txes: Tx[], migrationOperation: [string, MigrateOperation][]) =>
|
||||||
async (db: Db, productId: string, token: string, workspace: string): Promise<LoginInfo> => {
|
async (db: Db, productId: string, token: string, workspace: string): Promise<LoginInfo> => {
|
||||||
|
if (!/^[0-9a-z][0-9a-z-]{2,62}[0-9a-z]$/.test(workspace)) {
|
||||||
|
throw new PlatformError(new Status(Severity.ERROR, platform.status.InvalidId, { id: workspace }))
|
||||||
|
}
|
||||||
|
|
||||||
const { email, extra } = decodeToken(token)
|
const { email, extra } = decodeToken(token)
|
||||||
if (extra?.confirmed === false) {
|
const nonConfirmed = extra?.confirmed === false
|
||||||
|
console.log(`Creating workspace ${workspace} for ${email} ${nonConfirmed ? 'non confirmed' : 'confirmed'}`)
|
||||||
|
|
||||||
|
if (nonConfirmed) {
|
||||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email }))
|
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email }))
|
||||||
}
|
}
|
||||||
await createWorkspace(version, txes, migrationOperation, db, productId, workspace, '')
|
const info = await getAccount(db, email)
|
||||||
|
if (info === null) {
|
||||||
|
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email }))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.lastWorkspace !== undefined) {
|
||||||
|
if (Date.now() - info.lastWorkspace < 60 * 1000) {
|
||||||
|
throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceRateLimit, { workspace }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await createWorkspace(version, txes, migrationOperation, db, productId, workspace, '')
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err)
|
||||||
|
// We need to drop workspace, to prevent wrong data usage.
|
||||||
|
const ws = await getWorkspace(db, productId, workspace)
|
||||||
|
if (ws === null) {
|
||||||
|
throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace }))
|
||||||
|
}
|
||||||
|
await db.collection(WORKSPACE_COLLECTION).updateOne(
|
||||||
|
{
|
||||||
|
_id: ws._id
|
||||||
|
},
|
||||||
|
{ $set: { disabled: true } }
|
||||||
|
)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
info.lastWorkspace = Date.now()
|
||||||
|
|
||||||
|
// Update last workspace time.
|
||||||
|
await db.collection(ACCOUNT_COLLECTION).updateOne({ _id: info._id }, { $set: { lastWorkspace: Date.now() } })
|
||||||
|
|
||||||
await assignWorkspace(db, productId, email, workspace)
|
await assignWorkspace(db, productId, email, workspace)
|
||||||
await setRole(email, workspace, productId, AccountRole.Owner)
|
await setRole(email, workspace, productId, AccountRole.Owner)
|
||||||
const info = await getAccount(db, email)
|
|
||||||
const result = {
|
const result = {
|
||||||
endpoint: getEndpoint(),
|
endpoint: getEndpoint(),
|
||||||
email,
|
email,
|
||||||
token: generateToken(email, getWorkspaceId(workspace, productId), getExtra(info)),
|
token: generateToken(email, getWorkspaceId(workspace, productId), getExtra(info)),
|
||||||
productId
|
productId
|
||||||
}
|
}
|
||||||
|
console.log(`Creating workspace ${workspace} Done`)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,7 +726,9 @@ export async function getUserWorkspaces (db: Db, productId: string, token: strin
|
|||||||
.collection<Workspace>(WORKSPACE_COLLECTION)
|
.collection<Workspace>(WORKSPACE_COLLECTION)
|
||||||
.find(withProductId(productId, account.admin === true ? {} : { _id: { $in: account.workspaces } }))
|
.find(withProductId(productId, account.admin === true ? {} : { _id: { $in: account.workspaces } }))
|
||||||
.toArray()
|
.toArray()
|
||||||
).map(trimWorkspace)
|
)
|
||||||
|
.filter((it) => it.disabled !== true)
|
||||||
|
.map(trimWorkspace)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getWorkspaceAndAccount (
|
async function getWorkspaceAndAccount (
|
||||||
|
@ -57,17 +57,24 @@ export class MinioService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async list (workspaceId: WorkspaceId, prefix?: string): Promise<MinioWorkspaceItem[]> {
|
async list (workspaceId: WorkspaceId, prefix?: string): Promise<MinioWorkspaceItem[]> {
|
||||||
const items = new Map<string, BucketItem & { metaData: ItemBucketMetadata }>()
|
try {
|
||||||
const list = await this.client.listObjects(getBucketId(workspaceId), prefix, true)
|
const items = new Map<string, BucketItem & { metaData: ItemBucketMetadata }>()
|
||||||
await new Promise((resolve) => {
|
const list = await this.client.listObjects(getBucketId(workspaceId), prefix, true)
|
||||||
list.on('data', (data) => {
|
await new Promise((resolve) => {
|
||||||
items.set(data.name, { metaData: {}, ...data })
|
list.on('data', (data) => {
|
||||||
|
items.set(data.name, { metaData: {}, ...data })
|
||||||
|
})
|
||||||
|
list.on('end', () => {
|
||||||
|
resolve(null)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
list.on('end', () => {
|
return Array.from(items.values())
|
||||||
resolve(null)
|
} catch (err: any) {
|
||||||
})
|
if (((err?.message as string) ?? '').includes('Invalid bucket name')) {
|
||||||
})
|
return []
|
||||||
return Array.from(items.values())
|
}
|
||||||
|
throw err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async stat (workspaceId: WorkspaceId, objectName: string): Promise<BucketItemStat> {
|
async stat (workspaceId: WorkspaceId, objectName: string): Promise<BucketItemStat> {
|
||||||
|
Loading…
Reference in New Issue
Block a user