@ossy/workspaces 1.17.0 → 1.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +1 -0
  2. package/package.json +5 -4
  3. package/src/AuthenticationGuard.jsx +17 -5
  4. package/src/CreateWorkspacePage.jsx +11 -7
  5. package/src/GeneralSettings.jsx +6 -3
  6. package/src/Invitations.jsx +4 -2
  7. package/src/Invite.jsx +6 -4
  8. package/src/SelectWorkspacePage.jsx +5 -3
  9. package/src/Users.jsx +4 -2
  10. package/src/accept-invitation.action.js +1 -85
  11. package/src/accept-invitation.task.js +84 -0
  12. package/src/create-api-token.action.js +1 -17
  13. package/src/create-api-token.task.js +16 -0
  14. package/src/create.action.js +1 -30
  15. package/src/create.task.js +29 -0
  16. package/src/disable-service.action.js +1 -28
  17. package/src/disable-service.task.js +27 -0
  18. package/src/en.translations.json +6 -6
  19. package/src/enable-service.action.js +1 -28
  20. package/src/enable-service.task.js +27 -0
  21. package/src/get-api-tokens.action.js +1 -11
  22. package/src/get-api-tokens.task.js +10 -0
  23. package/src/get-invitations.action.js +1 -11
  24. package/src/get-invitations.task.js +10 -0
  25. package/src/get-resource-templates.action.js +1 -19
  26. package/src/get-resource-templates.task.js +18 -0
  27. package/src/get-users.action.js +1 -14
  28. package/src/get-users.task.js +13 -0
  29. package/src/get.action.js +1 -14
  30. package/src/get.task.js +13 -0
  31. package/src/import-resource-templates.action.js +1 -27
  32. package/src/import-resource-templates.task.js +26 -0
  33. package/src/index.js +14 -2
  34. package/src/invite-user.action.js +1 -85
  35. package/src/invite-user.task.js +84 -0
  36. package/src/list.action.js +1 -9
  37. package/src/list.task.js +8 -0
  38. package/src/remove-member.action.js +1 -17
  39. package/src/remove-member.task.js +16 -0
  40. package/src/server.js +3 -0
  41. package/src/sv.translations.json +6 -6
  42. package/src/sync-workspace-membership.task.js +4 -3
  43. package/src/workspaces.spec.js +71 -80
  44. package/src/current.api.js +0 -30
  45. package/src/invitations.api.js +0 -46
  46. package/src/item.api.js +0 -32
  47. package/src/list.api.js +0 -35
  48. package/src/members.api.js +0 -42
  49. package/src/resource-templates.api.js +0 -35
  50. package/src/services-disable.api.js +0 -23
  51. package/src/services-enable.api.js +0 -23
  52. package/src/tokens.api.js +0 -35
package/README.md CHANGED
@@ -26,6 +26,7 @@ Workspaces **feature package** for the Ossy platform — provides workspace crea
26
26
  | `AuthenticationGuard` | Wraps children, shows error for unauthenticated users |
27
27
  | `patchUserWorkspaceId` | Utility to update the active workspace cookie |
28
28
  | `Definition` | Feature metadata (id, actions, views) |
29
+ | `ListWorkspaces`, `GetWorkspace`, … | Platform action POJOs — use with `useSdk().read()` / `invoke()` |
29
30
 
30
31
  ## Usage
31
32
 
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@ossy/workspaces",
3
- "description": "Workspaces feature package create, select, and manage workspaces, users, and invitations",
4
- "version": "1.17.0",
3
+ "description": "Workspaces feature package - create, select, and manage workspaces, users, and invitations",
4
+ "version": "1.17.2",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "module": "./src/index.js",
8
8
  "exports": {
9
9
  ".": "./src/index.js",
10
+ "./server": "./src/server.js",
10
11
  "./workspace-invitation.email.jsx": "./src/workspace-invitation.email.jsx"
11
12
  },
12
13
  "scripts": {
@@ -25,7 +26,7 @@
25
26
  },
26
27
  "devDependencies": {
27
28
  "@jest/globals": "^30.2.0",
28
- "@ossy/platform": "^1.39.0",
29
+ "@ossy/platform": "^1.39.2",
29
30
  "casual": "^1.6.2",
30
31
  "jest": "^30.2.0"
31
32
  },
@@ -37,5 +38,5 @@
37
38
  "/src",
38
39
  "README.md"
39
40
  ],
40
- "gitHead": "d9d31182be64a448d575da432af2830d909ad4b9"
41
+ "gitHead": "2b8745d57fee8b6c08787e2755b4df489291a5cd"
41
42
  }
@@ -1,13 +1,25 @@
1
- import React from 'react'
2
- import { AuthenticationStatus, useAuthentication } from '@ossy/sdk-react'
1
+ import React, { useMemo } from 'react'
2
+ import { AuthenticationStatus, GetCurrentUser } from '@ossy/authentication'
3
+ import { useSdk, AsyncStatus } from '@ossy/sdk-react'
3
4
  import { Switch, View, Guide, Icon, useLocale } from '@ossy/design-system'
4
5
 
5
6
  export const AuthenticationGuard = ({ children }) => {
6
7
  const { t } = useLocale()
7
- const { status } = useAuthentication()
8
+ const sdk = useSdk()
9
+ const { status, data: user } = sdk.read(GetCurrentUser)
10
+
11
+ const authStatus = useMemo(() => {
12
+ if (status === AsyncStatus.Loading || status === AsyncStatus.NotInitialized) {
13
+ return AuthenticationStatus.Verifying
14
+ }
15
+ if (status === AsyncStatus.Success && user) {
16
+ return AuthenticationStatus.Authenticated
17
+ }
18
+ return AuthenticationStatus.NotAuthenticated
19
+ }, [status, user])
8
20
 
9
21
  return (
10
- <Switch on={status}>
22
+ <Switch on={authStatus}>
11
23
 
12
24
  <Switch.Case match={[AuthenticationStatus.NotAuthenticated]}>
13
25
  <View layout="off-center" style={{ height: '100%' }}>
@@ -31,7 +43,7 @@ export const AuthenticationGuard = ({ children }) => {
31
43
  </View>
32
44
  </Switch.Case>
33
45
 
34
- <Switch.Case match={[AuthenticationStatus.Verifying, AuthenticationStatus.NoInitialized]}>
46
+ <Switch.Case match={[AuthenticationStatus.Verifying, AuthenticationStatus.NotInitialized]}>
35
47
  <View layout="off-center" style={{ height: '100%' }}>
36
48
  <Icon
37
49
  name="spinner"
@@ -1,5 +1,6 @@
1
1
  import React, { useState } from 'react'
2
- import { useWorkspaces } from '@ossy/sdk-react'
2
+ import { ListWorkspaces, CreateWorkspace } from '@ossy/workspaces'
3
+ import { useSdk, cacheKey } from '@ossy/sdk-react'
3
4
  import {
4
5
  Button,
5
6
  useInputValue,
@@ -11,24 +12,26 @@ import {
11
12
  import { useRouter } from '@ossy/router-react'
12
13
  import { AuthenticationGuard } from './AuthenticationGuard.jsx'
13
14
  import { patchUserWorkspaceId } from './patchUserWorkspaceId.js'
15
+ import { metadata as CreateWorkspaceMeta } from './create.action.js'
14
16
 
15
17
  export const CreateWorkspacePage = () => {
16
18
  const router = useRouter()
17
19
  const { t } = useLocale()
18
20
  const [error, setError] = useState()
19
21
  const [workspaceName, setWorkspaceName] = useInputValue()
20
- const { createWorkspace } = useWorkspaces()
22
+ const sdk = useSdk()
21
23
 
22
24
  const onCreateWorkspace = workspaceName => {
23
- createWorkspace(workspaceName)
24
- .then((workspace) =>
25
- patchUserWorkspaceId(workspace.id).then((res) => {
25
+ sdk.invoke(CreateWorkspace, { name: workspaceName })
26
+ .then((workspace) => {
27
+ sdk.invalidate(cacheKey(ListWorkspaces))
28
+ return patchUserWorkspaceId(workspace.id).then((res) => {
26
29
  if (!res.ok) throw new Error('Could not select the new workspace')
27
30
  window.location.assign(
28
31
  router.getHref({ id: '@storage/home', params: { workspaceId: workspace.id } })
29
32
  )
30
33
  })
31
- )
34
+ })
32
35
  .catch(err => { setError(err.message) })
33
36
  }
34
37
 
@@ -36,7 +39,7 @@ export const CreateWorkspacePage = () => {
36
39
  <AuthenticationGuard>
37
40
  <View gap="m">
38
41
 
39
- <title>{t('workspaces.create.documentTitle')}</title>
42
+ <title>{t('profile/workspaces/create.documentTitle')}</title>
40
43
  <Title variant="primary" className="stack-m">{t('workspaces.create.title')}</Title>
41
44
 
42
45
  <Input
@@ -61,6 +64,7 @@ export const CreateWorkspacePage = () => {
61
64
 
62
65
  <Button
63
66
  id="createWorkspace"
67
+ data-action={CreateWorkspaceMeta.id}
64
68
  variant="cta"
65
69
  onClick={() => onCreateWorkspace(workspaceName)}>{t('workspaces.create.submit')}
66
70
  </Button>
@@ -1,5 +1,7 @@
1
1
  import React, { useState } from 'react'
2
- import { useWorkspace, useUser, AsyncStatus } from '@ossy/sdk-react'
2
+ import { GetWorkspace } from '@ossy/workspaces'
3
+ import { GetCurrentUser } from '@ossy/authentication'
4
+ import { useSdk, AsyncStatus } from '@ossy/sdk-react'
3
5
  import { Switch, Title, Button, View, Text, useLocale } from '@ossy/design-system'
4
6
 
5
7
  const formatDate = (timestamp) => timestamp ? new Date(timestamp).toLocaleDateString() : ''
@@ -7,8 +9,9 @@ const FlowStage = { View: 'View', Edit: 'Edit' }
7
9
 
8
10
  export const GeneralSettings = () => {
9
11
  const { t } = useLocale()
10
- const { workspace } = useWorkspace()
11
- const { status: userStatus, user } = useUser()
12
+ const sdk = useSdk()
13
+ const { data: workspace } = sdk.read(GetWorkspace)
14
+ const { status: userStatus } = sdk.read(GetCurrentUser)
12
15
  const [flowStage, setFlowStage] = useState(FlowStage.View)
13
16
  const services = workspace?.services || {}
14
17
 
@@ -1,12 +1,14 @@
1
1
  import React from 'react'
2
- import { useWorkspace } from '@ossy/sdk-react'
2
+ import { GetWorkspace } from '@ossy/workspaces'
3
+ import { useSdk } from '@ossy/sdk-react'
3
4
  import { Title, Text, Button, View, Icon, useLocale } from '@ossy/design-system'
4
5
  import { useRouter } from '@ossy/router-react'
5
6
 
6
7
  export const Invitations = () => {
7
8
  const { t } = useLocale()
8
9
  const router = useRouter()
9
- const { workspace } = useWorkspace()
10
+ const sdk = useSdk()
11
+ const { data: workspace } = sdk.read(GetWorkspace)
10
12
  const invitations = workspace?.invitations || []
11
13
 
12
14
  return (
package/src/Invite.jsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import React, { useState, useRef } from 'react'
2
- import { useWorkspace } from '@ossy/sdk-react'
2
+ import { InviteUser } from '@ossy/workspaces'
3
+ import { useSdk } from '@ossy/sdk-react'
3
4
  import { useRouter } from '@ossy/router-react'
4
5
  import {
5
6
  useInputValue,
@@ -22,14 +23,14 @@ const FlowStage = {
22
23
  export const Invite = () => {
23
24
  const { t } = useLocale()
24
25
  const router = useRouter()
25
- const { inviteUser } = useWorkspace()
26
+ const sdk = useSdk()
26
27
  const [flowStage, setFlowStage] = useState(FlowStage.EnterEmail)
27
28
  const [email, setEmail] = useInputValue('')
28
29
  const formRef = useRef()
29
30
 
30
31
  const sendInvite = event => {
31
32
  event.preventDefault()
32
- formRef.current.reportValidity() && inviteUser(email)
33
+ formRef.current.reportValidity() && sdk.invoke(InviteUser, { email })
33
34
  .then(() => setFlowStage(FlowStage.Success))
34
35
  .catch(() => setFlowStage(FlowStage.Error))
35
36
  }
@@ -37,7 +38,7 @@ export const Invite = () => {
37
38
  return (
38
39
  <Switch on={flowStage}>
39
40
 
40
- <title>{t('workspace.invitations.add.documentTitle')}</title>
41
+ <title>{t('workspace/invitations/add.documentTitle')}</title>
41
42
 
42
43
  <Switch.Case match={[FlowStage.EnterEmail]}>
43
44
  <View gap="m">
@@ -73,6 +74,7 @@ export const Invite = () => {
73
74
  <Button
74
75
  variant="cta"
75
76
  id="registerButton"
77
+ data-action={InviteUser.id}
76
78
  type="submit"
77
79
  onClick={sendInvite}>{t('workspace.invite.submit')}
78
80
  </Button>
@@ -1,5 +1,6 @@
1
1
  import React from 'react'
2
- import { useWorkspaces, AsyncStatus } from '@ossy/sdk-react'
2
+ import { ListWorkspaces } from '@ossy/workspaces'
3
+ import { useSdk, AsyncStatus } from '@ossy/sdk-react'
3
4
  import { Title, Text, Switch, Button, View, Guide, Icon, useLocale } from '@ossy/design-system'
4
5
  import { useRouter } from '@ossy/router-react'
5
6
  import { AuthenticationGuard } from './AuthenticationGuard.jsx'
@@ -15,7 +16,8 @@ const PageFlow = {
15
16
  export const SelectWorkspacePage = () => {
16
17
  const router = useRouter()
17
18
  const { t } = useLocale()
18
- const { status: asyncStatus, workspaces } = useWorkspaces()
19
+ const sdk = useSdk()
20
+ const { status: asyncStatus, data: workspaces = [] } = sdk.read(ListWorkspaces)
19
21
 
20
22
  const pageFlow = (asyncStatus === AsyncStatus.Success && workspaces.length === 0)
21
23
  ? PageFlow.NoWorkspacesCreated
@@ -31,7 +33,7 @@ export const SelectWorkspacePage = () => {
31
33
  return (
32
34
  <AuthenticationGuard>
33
35
 
34
- <title>{t('workspaces.select.documentTitle')}</title>
36
+ <title>{t('profile/workspaces.documentTitle')}</title>
35
37
 
36
38
  <Switch on={pageFlow}>
37
39
  <Switch.Case match={[PageFlow.Success]}>
package/src/Users.jsx CHANGED
@@ -1,12 +1,14 @@
1
1
  import React from 'react'
2
- import { useUsers, AsyncStatus } from '@ossy/sdk-react'
2
+ import { GetUsers } from '@ossy/workspaces'
3
+ import { useSdk, AsyncStatus } from '@ossy/sdk-react'
3
4
  import { Text, View, Icon, Title, Button, useLocale } from '@ossy/design-system'
4
5
  import { useRouter } from '@ossy/router-react'
5
6
 
6
7
  export const Users = () => {
7
8
  const { t } = useLocale()
8
9
  const router = useRouter()
9
- const { users = [], status } = useUsers()
10
+ const sdk = useSdk()
11
+ const { status, data: users = [] } = sdk.read(GetUsers)
10
12
 
11
13
  if (status === AsyncStatus.Loading) return <>{t('workspace.users.loading')}</>
12
14
 
@@ -1,85 +1 @@
1
- import jwt from 'jsonwebtoken'
2
- import { Aggregate } from '@ossy/event-store'
3
- import { Workspace, WorkspacesEvents } from '@ossy/workspaces'
4
- import { User, UsersEvents } from '@ossy/users'
5
- import { Token, TokenEvents } from '@ossy/tokens'
6
- import { ConfigService } from '@ossy/platform'
7
- import { createLogger } from '@ossy/observability'
8
-
9
- const log = createLogger('workspaces/accept-invitation')
10
-
11
- function verifyToken(token) {
12
- return new Promise((resolve, reject) => {
13
- if (!token) return reject(new Error('No token'))
14
- jwt.verify(token, ConfigService.TokenSecret, { algorithms: ['HS256'] }, (err, payload) => {
15
- if (err) return reject(err)
16
- resolve(payload)
17
- })
18
- })
19
- }
20
-
21
- export const id = 'workspaces/accept-invitation'
22
- export const access = 'public'
23
-
24
- export async function run({ payload, req }) {
25
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
26
- const verificationToken = payload?.token ?? req?.query?.token
27
-
28
- const { aud: tokenWorkspaceId, sub: email } = await verifyToken(verificationToken)
29
- .catch(() => { throw Object.assign(new Error('Invalid token'), { status: 401 }) })
30
-
31
- const resolvedWorkspaceId = tokenWorkspaceId ?? workspaceId
32
- const workspace = await Aggregate.Of(Workspace, resolvedWorkspaceId).then(Aggregate.View())
33
-
34
- if (!email) throw Object.assign(new Error('No email found in token payload'), { status: 401 })
35
-
36
- const existingUser = await Aggregate.Collection.findOne({ type: 'User', 'state.email': email }).then(agg => agg?.state)
37
-
38
- if (existingUser && workspace.users.includes(existingUser.id)) {
39
- throw Object.assign(new Error('User already in workspace'), { status: 400 })
40
- }
41
-
42
- let userId
43
- let signInToken
44
- let signInExpiresAt
45
-
46
- if (existingUser) {
47
- userId = existingUser.id
48
- const signInExpiresIn = ConfigService.TokenValidity
49
- signInExpiresAt = Date.now() + signInExpiresIn * 1000
50
- signInToken = jwt.sign({ sub: userId, type: 'WebAuth' }, ConfigService.TokenSecret, { expiresIn: signInExpiresIn })
51
- const signInEvent = TokenEvents.Created({
52
- type: 'WebAuth',
53
- subject: userId,
54
- createdBy: userId,
55
- token: signInToken,
56
- expiresAt: signInExpiresAt,
57
- })
58
- const userInvitationAccepted = WorkspacesEvents.UserInvitationAccepted({ email, createdBy: userId })
59
- await Promise.all([
60
- Aggregate.Of(Token, signInEvent),
61
- Aggregate.Of(Workspace, resolvedWorkspaceId).then(Aggregate.Add(userInvitationAccepted)),
62
- ])
63
- } else {
64
- const signUpEvent = UsersEvents.SignedUp({ email })
65
- userId = signUpEvent.createdBy
66
- const signInExpiresIn = ConfigService.TokenValidity
67
- signInExpiresAt = Date.now() + signInExpiresIn * 1000
68
- signInToken = jwt.sign({ sub: userId, type: 'WebAuth' }, ConfigService.TokenSecret, { expiresIn: signInExpiresIn })
69
- const signInEvent = TokenEvents.Created({
70
- type: 'WebAuth',
71
- subject: userId,
72
- createdBy: userId,
73
- token: signInToken,
74
- expiresAt: signInExpiresAt,
75
- })
76
- const userInvitationAccepted = WorkspacesEvents.UserInvitationAccepted({ email, createdBy: userId })
77
- await Promise.all([
78
- Aggregate.Of(User, signUpEvent),
79
- Aggregate.Of(Token, signInEvent),
80
- Aggregate.Of(Workspace, resolvedWorkspaceId).then(Aggregate.Add(userInvitationAccepted)),
81
- ])
82
- }
83
-
84
- return { authToken: signInToken, authExpiresAt: signInExpiresAt }
85
- }
1
+ export const metadata = { id: 'workspaces/accept-invitation', access: 'public' }
@@ -0,0 +1,84 @@
1
+ import jwt from 'jsonwebtoken'
2
+ import { Aggregate } from '@ossy/event-store'
3
+ import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
4
+ import { User, UsersEvents } from '@ossy/users/server'
5
+ import { Token, TokenEvents } from '@ossy/tokens/server'
6
+ import { ConfigService } from '@ossy/platform'
7
+ import { createLogger } from '@ossy/observability'
8
+
9
+ export const metadata = { id: 'workspaces/accept-invitation' }
10
+
11
+ const log = createLogger('workspaces/accept-invitation')
12
+
13
+ function verifyToken(token) {
14
+ return new Promise((resolve, reject) => {
15
+ if (!token) return reject(new Error('No token'))
16
+ jwt.verify(token, ConfigService.TokenSecret, { algorithms: ['HS256'] }, (err, payload) => {
17
+ if (err) return reject(err)
18
+ resolve(payload)
19
+ })
20
+ })
21
+ }
22
+
23
+ export async function run({ payload, req }) {
24
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
25
+ const verificationToken = payload?.token ?? req?.query?.token
26
+
27
+ const { aud: tokenWorkspaceId, sub: email } = await verifyToken(verificationToken)
28
+ .catch(() => { throw Object.assign(new Error('Invalid token'), { status: 401 }) })
29
+
30
+ const resolvedWorkspaceId = tokenWorkspaceId ?? workspaceId
31
+ const workspace = await Aggregate.Of(Workspace, resolvedWorkspaceId).then(Aggregate.View())
32
+
33
+ if (!email) throw Object.assign(new Error('No email found in token payload'), { status: 401 })
34
+
35
+ const existingUser = await Aggregate.Collection.findOne({ type: 'User', 'state.email': email }).then(agg => agg?.state)
36
+
37
+ if (existingUser && workspace.users.includes(existingUser.id)) {
38
+ throw Object.assign(new Error('User already in workspace'), { status: 400 })
39
+ }
40
+
41
+ let userId
42
+ let signInToken
43
+ let signInExpiresAt
44
+
45
+ if (existingUser) {
46
+ userId = existingUser.id
47
+ const signInExpiresIn = ConfigService.TokenValidity
48
+ signInExpiresAt = Date.now() + signInExpiresIn * 1000
49
+ signInToken = jwt.sign({ sub: userId, type: 'WebAuth' }, ConfigService.TokenSecret, { expiresIn: signInExpiresIn })
50
+ const signInEvent = TokenEvents.Created({
51
+ type: 'WebAuth',
52
+ subject: userId,
53
+ createdBy: userId,
54
+ token: signInToken,
55
+ expiresAt: signInExpiresAt,
56
+ })
57
+ const userInvitationAccepted = WorkspacesEvents.UserInvitationAccepted({ email, createdBy: userId })
58
+ await Promise.all([
59
+ Aggregate.Of(Token, signInEvent),
60
+ Aggregate.Of(Workspace, resolvedWorkspaceId).then(Aggregate.Add(userInvitationAccepted)),
61
+ ])
62
+ } else {
63
+ const signUpEvent = UsersEvents.SignedUp({ email })
64
+ userId = signUpEvent.createdBy
65
+ const signInExpiresIn = ConfigService.TokenValidity
66
+ signInExpiresAt = Date.now() + signInExpiresIn * 1000
67
+ signInToken = jwt.sign({ sub: userId, type: 'WebAuth' }, ConfigService.TokenSecret, { expiresIn: signInExpiresIn })
68
+ const signInEvent = TokenEvents.Created({
69
+ type: 'WebAuth',
70
+ subject: userId,
71
+ createdBy: userId,
72
+ token: signInToken,
73
+ expiresAt: signInExpiresAt,
74
+ })
75
+ const userInvitationAccepted = WorkspacesEvents.UserInvitationAccepted({ email, createdBy: userId })
76
+ await Promise.all([
77
+ Aggregate.Of(User, signUpEvent),
78
+ Aggregate.Of(Token, signInEvent),
79
+ Aggregate.Of(Workspace, resolvedWorkspaceId).then(Aggregate.Add(userInvitationAccepted)),
80
+ ])
81
+ }
82
+
83
+ return { authToken: signInToken, authExpiresAt: signInExpiresAt }
84
+ }
@@ -1,17 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Workspace, WorkspacesEvents } from '@ossy/workspaces'
3
-
4
- export const id = 'workspaces/create-api-token'
5
- export const access = 'workspace'
6
-
7
- export async function run({ payload, req }) {
8
- const createdBy = payload?.userId ?? req?.userId
9
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
10
- const description = payload?.description
11
-
12
- const event = WorkspacesEvents.ApiTokenCreated({ createdBy, workspaceId, description })
13
-
14
- const aggregate = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.Add(event))
15
- const last = aggregate.events[aggregate.events.length - 1]
16
- return { id: last.id, token: event.payload.token }
17
- }
1
+ export const metadata = { id: 'workspaces/create-api-token', access: 'workspace' }
@@ -0,0 +1,16 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
3
+
4
+ export const metadata = { id: 'workspaces/create-api-token' }
5
+
6
+ export async function run({ payload, req }) {
7
+ const createdBy = payload?.userId ?? req?.userId
8
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
9
+ const description = payload?.description
10
+
11
+ const event = WorkspacesEvents.ApiTokenCreated({ createdBy, workspaceId, description })
12
+
13
+ const aggregate = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.Add(event))
14
+ const last = aggregate.events[aggregate.events.length - 1]
15
+ return { id: last.id, token: event.payload.token }
16
+ }
@@ -1,30 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Workspace, WorkspacesEvents } from '@ossy/workspaces'
3
- import { ConfigService } from '@ossy/platform'
4
-
5
- export const id = 'workspaces/create'
6
- export const access = 'authenticated'
7
-
8
- export async function run({ payload, req }) {
9
- const createdBy = payload?.userId ?? req?.userId
10
- const name = (payload?.name ?? req?.body?.name)?.trim?.()
11
-
12
- if (!name || typeof name !== 'string') {
13
- throw Object.assign(new Error('Invalid name'), { status: 400 })
14
- }
15
-
16
- const createdEvent = WorkspacesEvents.Created({ createdBy, name })
17
- const botUserInvitedEvent = WorkspacesEvents.UserInvited({
18
- createdBy: ConfigService.BotUserId,
19
- email: ConfigService.BotUserEmail,
20
- })
21
- const botUserAcceptedEvent = WorkspacesEvents.UserInvitationAccepted({
22
- createdBy: ConfigService.BotUserId,
23
- email: ConfigService.BotUserEmail,
24
- })
25
-
26
- return Aggregate.Of(Workspace, createdEvent)
27
- .then(Aggregate.Add(botUserInvitedEvent))
28
- .then(Aggregate.Add(botUserAcceptedEvent))
29
- .then(Aggregate.View())
30
- }
1
+ export const metadata = { id: 'workspaces/create', access: 'authenticated' }
@@ -0,0 +1,29 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
3
+ import { ConfigService } from '@ossy/platform'
4
+
5
+ export const metadata = { id: 'workspaces/create' }
6
+
7
+ export async function run({ payload, req }) {
8
+ const createdBy = payload?.userId ?? req?.userId
9
+ const name = (payload?.name ?? req?.body?.name)?.trim?.()
10
+
11
+ if (!name || typeof name !== 'string') {
12
+ throw Object.assign(new Error('Invalid name'), { status: 400 })
13
+ }
14
+
15
+ const createdEvent = WorkspacesEvents.Created({ createdBy, name })
16
+ const botUserInvitedEvent = WorkspacesEvents.UserInvited({
17
+ createdBy: ConfigService.BotUserId,
18
+ email: ConfigService.BotUserEmail,
19
+ })
20
+ const botUserAcceptedEvent = WorkspacesEvents.UserInvitationAccepted({
21
+ createdBy: ConfigService.BotUserId,
22
+ email: ConfigService.BotUserEmail,
23
+ })
24
+
25
+ return Aggregate.Of(Workspace, createdEvent)
26
+ .then(Aggregate.Add(botUserInvitedEvent))
27
+ .then(Aggregate.Add(botUserAcceptedEvent))
28
+ .then(Aggregate.View())
29
+ }
@@ -1,28 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Workspace, WorkspacesEvents } from '@ossy/workspaces'
3
-
4
- export const id = 'workspaces/disable-service'
5
- export const access = 'workspace'
6
-
7
- const AVAILABLE_SERVICES = [
8
- '@ossy/tasks/visual-content-descriptors',
9
- '@ossy/tasks/resize-common-web',
10
- '@ossy/resumes',
11
- '@ossy/consultancy',
12
- '@ossy/apps',
13
- '@ossy/domains',
14
- ]
15
-
16
- export async function run({ payload, req }) {
17
- const createdBy = payload?.userId ?? req?.userId
18
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
19
- const service = payload?.service
20
-
21
- if (!AVAILABLE_SERVICES.includes(service)) {
22
- throw Object.assign(new Error(`Service ${service} is not available`), { status: 400 })
23
- }
24
-
25
- const event = WorkspacesEvents.ServiceDisabled({ createdBy, service })
26
- await Aggregate.Of(Workspace, workspaceId).then(Aggregate.Add(event))
27
- return null
28
- }
1
+ export const metadata = { id: 'workspaces/disable-service', access: 'workspace' }
@@ -0,0 +1,27 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
3
+
4
+ export const metadata = { id: 'workspaces/disable-service' }
5
+
6
+ const AVAILABLE_SERVICES = [
7
+ '@ossy/tasks/visual-content-descriptors',
8
+ '@ossy/tasks/resize-common-web',
9
+ '@ossy/resumes',
10
+ '@ossy/consultancy',
11
+ '@ossy/apps',
12
+ '@ossy/domains',
13
+ ]
14
+
15
+ export async function run({ payload, req }) {
16
+ const createdBy = payload?.userId ?? req?.userId
17
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
18
+ const service = payload?.service
19
+
20
+ if (!AVAILABLE_SERVICES.includes(service)) {
21
+ throw Object.assign(new Error(`Service ${service} is not available`), { status: 400 })
22
+ }
23
+
24
+ const event = WorkspacesEvents.ServiceDisabled({ createdBy, service })
25
+ await Aggregate.Of(Workspace, workspaceId).then(Aggregate.Add(event))
26
+ return null
27
+ }
@@ -1,10 +1,10 @@
1
1
  {
2
- "workspaces.create.documentTitle": "Create workspace",
2
+ "profile/workspaces/create.documentTitle": "Create workspace",
3
3
  "workspaces.create.title": "Create a new workspace",
4
4
  "workspaces.create.namePlaceholder": "Workspace name",
5
5
  "workspaces.create.cancel": "Cancel",
6
6
  "workspaces.create.submit": "Create",
7
- "workspaces.select.documentTitle": "Select workspace",
7
+ "profile/workspaces.documentTitle": "Select workspace",
8
8
  "workspaces.select.title": "My workspaces",
9
9
  "workspaces.select.newWorkspace": "New workspace",
10
10
  "workspaces.select.loading": "Loading...",
@@ -43,15 +43,15 @@
43
43
  "workspaces/remove-member.description": "Remove a user from the workspace",
44
44
  "workspaces/list.label": "List workspaces",
45
45
  "workspaces/list.description": "List workspaces for the authenticated user",
46
- "workspace.settings.documentTitle": "Workspace settings",
46
+ "workspace/settings.documentTitle": "Workspace settings",
47
47
  "workspace.settings.title": "Workspace settings",
48
- "workspace.users.documentTitle": "Users",
48
+ "workspace/users.documentTitle": "Users",
49
49
  "workspace.users.title": "Users",
50
50
  "workspace.users.description": "Manage users in your workspace.",
51
51
  "workspace.users.invite": "Invite user",
52
52
  "workspace.users.loading": "Loading",
53
- "workspace.invitations.documentTitle": "Invitations",
54
- "workspace.invitations.add.documentTitle": "Invite user",
53
+ "workspace/invitations.documentTitle": "Invitations",
54
+ "workspace/invitations/add.documentTitle": "Invite user",
55
55
  "workspace.invitations.title": "Invitations",
56
56
  "workspace.settings.detailsTitle": "Workspace details",
57
57
  "workspace.settings.edit": "Edit",
@@ -1,28 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Workspace, WorkspacesEvents } from '@ossy/workspaces'
3
-
4
- export const id = 'workspaces/enable-service'
5
- export const access = 'workspace'
6
-
7
- const AVAILABLE_SERVICES = [
8
- '@ossy/tasks/visual-content-descriptors',
9
- '@ossy/tasks/resize-common-web',
10
- '@ossy/resumes',
11
- '@ossy/consultancy',
12
- '@ossy/apps',
13
- '@ossy/domains',
14
- ]
15
-
16
- export async function run({ payload, req }) {
17
- const createdBy = payload?.userId ?? req?.userId
18
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
19
- const service = payload?.service ?? payload
20
-
21
- if (!AVAILABLE_SERVICES.includes(service)) {
22
- throw Object.assign(new Error(`Service ${service} is not available`), { status: 400 })
23
- }
24
-
25
- const event = WorkspacesEvents.ServiceEnabled({ createdBy, service })
26
- await Aggregate.Of(Workspace, workspaceId).then(Aggregate.Add(event))
27
- return null
28
- }
1
+ export const metadata = { id: 'workspaces/enable-service', access: 'workspace' }