@ossy/platform 3.0.1 → 3.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/platform",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Ossy application server runtime",
5
5
  "repository": {
6
6
  "type": "git",
@@ -44,16 +44,17 @@
44
44
  "@aws-sdk/util-create-request": "^3.972.26",
45
45
  "@aws-sdk/util-format-url": "^3.972.17",
46
46
  "@modelcontextprotocol/sdk": "^1.12.1",
47
- "@ossy/app": "^3.0.1",
48
- "@ossy/event-store": "^3.0.1",
49
- "@ossy/locale": "^3.0.1",
50
- "@ossy/observability": "^3.0.1",
51
- "@ossy/policies": "^3.0.1",
52
- "@ossy/schema": "^3.0.1",
53
- "@ossy/sdk": "^3.0.1",
54
- "@ossy/tokens": "^3.0.1",
55
- "@ossy/users": "^3.0.1",
56
- "@ossy/workspaces": "^3.0.1",
47
+ "@ossy/config": "^3.0.2",
48
+ "@ossy/event-store": "^3.0.2",
49
+ "@ossy/locale": "^3.0.2",
50
+ "@ossy/manifest": "^3.0.2",
51
+ "@ossy/observability": "^3.0.2",
52
+ "@ossy/policies": "^3.0.2",
53
+ "@ossy/schema": "^3.0.2",
54
+ "@ossy/sdk": "^3.0.2",
55
+ "@ossy/tokens": "^3.0.2",
56
+ "@ossy/users": "^3.0.2",
57
+ "@ossy/workspaces": "^3.0.2",
57
58
  "cookie-parser": "^1.4.7",
58
59
  "dotenv": ">=16.0.0 <18.0.0",
59
60
  "express": ">=5.0.0 <6.0.0",
@@ -73,5 +74,5 @@
73
74
  "src",
74
75
  "Dockerfile"
75
76
  ],
76
- "gitHead": "4a70ec216448680d2fddc57b2a8a0077489720ae"
77
+ "gitHead": "f0a8af3c370bbaffe4f7388c14f9345a5c6c56e5"
77
78
  }
@@ -0,0 +1,46 @@
1
+ import {
2
+ ActionService,
3
+ mergeUserAppSettingsCookie,
4
+ setAuthCookie,
5
+ } from '@ossy/platform'
6
+
7
+ export const metadata = {
8
+ id: 'users.accept-invitation',
9
+ path: '/api/v0/users/accept-invitation',
10
+ action: '@ossy/workspaces/actions/accept-invitation',
11
+ method: 'GET',
12
+ query: ['token', 'workspaceId', 'redirect'],
13
+ }
14
+
15
+ export default async function handle (req, res) {
16
+ if (req.method !== 'GET') {
17
+ res.setHeader('Allow', 'GET')
18
+ res.status(405).json({ error: 'Method Not Allowed' })
19
+ return
20
+ }
21
+
22
+ try {
23
+ const { authToken, workspaceId } = await ActionService.invoke('@ossy/workspaces/actions/accept-invitation', {
24
+ payload: {
25
+ token: req.query.token,
26
+ workspaceId: req.query.workspaceId,
27
+ },
28
+ req,
29
+ })
30
+
31
+ setAuthCookie(res, authToken)
32
+ if (workspaceId) {
33
+ mergeUserAppSettingsCookie(req, res, { workspaceId })
34
+ }
35
+
36
+ const redirect = req.query.redirect
37
+ if (redirect && typeof redirect === 'string') {
38
+ res.redirect(302, redirect)
39
+ return
40
+ }
41
+
42
+ res.status(200).json('')
43
+ } catch (err) {
44
+ res.status(err.status || 401).json('')
45
+ }
46
+ }
@@ -1,48 +1 @@
1
- /**
2
- * API Configuration
3
- * @class
4
- */
5
- export class ConfigService {
6
-
7
- static Port = process.env.BE_PORT || 3000
8
- static Domain = process.env.DOMAIN || `localhost:${ConfigService.Port}`
9
- static WebClientDomain = process.env.WEB_CLIENT_DOMAIN || `http://localhost:3000`
10
- static MongoUrl = process.env.DB_URL || 'mongodb://mongodb:27017/'
11
- static DbName = process.env.DB_NAME || 'test'
12
- static TokenSecret = process.env.TOKEN_SECRET || 'testsecret2'
13
- static TokenValidity = process.env.TOKEN_VALIDITY || 60 * 60 * 24 * 14
14
- static LimitedAccessCode = process.env.LIMITED_ACCESS_CODE || 'test'
15
- static BuildEnvironment = process.env.BUILD_ENVIRONMENT
16
- static BotUserEmail = process.env.BOT_USER_EMAIL || 'ossybot@ossy.se'
17
- static BotUserId = process.env.BOT_USER_ID || 'Mil5qAL7jDFCTyuKD_BKb'
18
- /** Set on `req.userId` when there is no valid session; role checks can treat this as unauthenticated. */
19
- static AnonymousUserId = process.env.ANONYMOUS_USER_ID || 'anonymous'
20
- static MediaRepository = process.env.MEDIA_REPOSITORY
21
- static MediaCdnDomainName = process.env.MEDIA_CDN_DOMAIN_NAME
22
- static awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID
23
- static awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY
24
- static SesRegion = process.env.SES_REGION || 'eu-north-1'
25
- static Debug = process.env.DEBUG
26
-
27
- /**
28
- * Derives the web client base URL from the incoming request's Origin or Referer
29
- * header so that email links point back to whichever domain the user came from
30
- * (e.g. http://ossy.local, http://localhost:3002). Falls back to the
31
- * WEB_CLIENT_DOMAIN env var / default when no header is present.
32
- */
33
- static getWebClientBaseUrl(req) {
34
- const origin = req?.headers?.origin
35
- if (origin && origin !== 'null') return origin
36
-
37
- const referer = req?.headers?.referer
38
- if (referer) {
39
- try {
40
- const { origin: refOrigin } = new URL(referer)
41
- if (refOrigin && refOrigin !== 'null') return refOrigin
42
- } catch {}
43
- }
44
-
45
- return ConfigService.WebClientDomain
46
- }
47
-
48
- }
1
+ export { ConfigService } from '@ossy/config'
@@ -3,7 +3,7 @@ import { jsonSchemaToZodShape } from './json-schema-to-zod.js'
3
3
  import {
4
4
  TASK_TOPOLOGY_RESOURCE_URI,
5
5
  capabilitiesTaskTopologyResource,
6
- } from '@ossy/app/manifest/build-capabilities'
6
+ } from '@ossy/manifest/build-capabilities'
7
7
 
8
8
  /**
9
9
  * @param {{
@@ -1,4 +1,4 @@
1
- import { buildCapabilities } from '@ossy/app/manifest/build-capabilities'
1
+ import { buildCapabilities } from '@ossy/manifest/build-capabilities'
2
2
  import { mountOssyMcp } from './mount-ossy-mcp.js'
3
3
  import { uploadFileToolHandler, UPLOAD_FILE_TOOL } from './upload-file-tool.js'
4
4
  import { ActionService } from '../actions/action.service.js'
@@ -1,26 +1 @@
1
- import { createLogger } from '@ossy/observability'
2
-
3
- const log = createLogger('platform')
4
-
5
- /** @type {object[]} */
6
- const systemSchemas = []
7
-
8
- /**
9
- * Register a single schema POJO (as inlined in `build/manifest.json`).
10
- *
11
- * @param {object} schema
12
- */
13
- export function registerSchema (schema) {
14
- if (!schema || typeof schema !== 'object' || Array.isArray(schema)) return
15
- if (typeof schema.id !== 'string' || schema.id.trim() === '') return
16
- if (systemSchemas.find(s => s.id === schema.id)) return
17
- systemSchemas.push(schema)
18
- log.info(`[SchemaRegistry] Registered system schema: ${schema.id}`)
19
- }
20
-
21
- /**
22
- * @returns {object[]}
23
- */
24
- export function getSystemSchemas () {
25
- return systemSchemas
26
- }
1
+ export { registerSchema, getSystemSchemas } from '@ossy/schema'
@@ -1,90 +1,6 @@
1
- import { Schema } from '@ossy/schema'
2
-
3
- /** Field input types accepted in schema definitions (UI + API contract). */
4
- export const ALLOWED_FIELD_TYPES = new Set([
5
- 'text',
6
- 'email',
7
- 'textarea',
8
- 'richtext',
9
- 'number',
10
- 'select',
11
- 'multiselect',
12
- 'file',
13
- 'image',
14
- 'boolean',
15
- 'date',
16
- 'timestamp',
17
- 'date-range',
18
- 'date-ranges',
19
- 'id',
20
- 'path',
21
- ])
22
-
23
- /**
24
- * Normalize template field `type` to the canonical editor type.
25
- * `image` is an alias for `file` with default `accept: 'image/*'` when accept is omitted.
26
- *
27
- * @param {string | undefined} type
28
- * @returns {{ type: string, defaultAccept?: string }}
29
- */
30
- export function normalizeFieldType (type) {
31
- const raw = typeof type === 'string' ? type.trim() : ''
32
- if (raw === 'image') return { type: 'file', defaultAccept: 'image/*' }
33
- return { type: raw }
34
- }
35
-
36
- /**
37
- * @param {{ type?: string, accept?: string, min?: number, max?: number } & Record<string, unknown>} field
38
- */
39
- export function resolveFieldDef (field) {
40
- const { type: canonical, defaultAccept } = normalizeFieldType(field?.type)
41
- return {
42
- ...field,
43
- type: canonical,
44
- accept: field?.accept ?? defaultAccept,
45
- max: field?.max ?? 1,
46
- }
47
- }
48
-
49
- /**
50
- * Validates a batch import of workspace schemas.
51
- *
52
- * @param {unknown} templates
53
- * @param {Set<string>} reservedIds - System schema ids that must not be redefined
54
- * @returns {{ ok: true } | { ok: false, code: string, message: string }}
55
- */
56
- export function validateSchemasForImport (templates, reservedIds) {
57
- if (!Array.isArray(templates)) {
58
- return { ok: false, code: 'INVALID_PAYLOAD', message: 'Body must be a JSON array of schemas' }
59
- }
60
-
61
- const engine = Schema.of({ schemas: templates })
62
- const seenIds = new Set()
63
-
64
- for (const template of templates) {
65
- if (reservedIds?.has(template?.id)) {
66
- return {
67
- ok: false,
68
- code: 'RESERVED_SCHEMA_ID',
69
- message: `Template id "${template.id}" is reserved by a system template`,
70
- }
71
- }
72
-
73
- const result = engine.validate(template)
74
- if (!result.ok) {
75
- const first = result.errors[0]
76
- return { ok: false, code: first.code, message: first.message }
77
- }
78
-
79
- if (seenIds.has(template.id)) {
80
- return {
81
- ok: false,
82
- code: 'DUPLICATE_SCHEMA_ID',
83
- message: `Duplicate schema id "${template.id}" in import payload`,
84
- }
85
- }
86
- seenIds.add(template.id)
87
- }
88
-
89
- return { ok: true }
90
- }
1
+ export {
2
+ validateSchemasForImport,
3
+ ALLOWED_FIELD_TYPES,
4
+ normalizeFieldType,
5
+ resolveFieldDef,
6
+ } from '@ossy/schema'
package/src/runtime.js CHANGED
@@ -6,7 +6,7 @@ import morgan from 'morgan'
6
6
  import { Router as OssyRouter } from '@ossy/router'
7
7
  import { loadManifest, resolveEntryUrl, loadLayoutsById, resolvePageLayoutRender } from './server.js'
8
8
  import { resolveRequestLocale } from './locale.js'
9
- import { buildManifestSummary } from '@ossy/app/manifest/build-manifest-summary'
9
+ import { buildManifestSummary } from '@ossy/manifest/build-manifest-summary'
10
10
  import { ProxyInternal } from './proxy-internal.js'
11
11
  import { loadSite, invalidateSite } from './site-loader.js'
12
12
  import { createLogger } from '@ossy/observability'
@@ -0,0 +1,50 @@
1
+ import {
2
+ ActionService,
3
+ mergeUserAppSettingsCookie,
4
+ } from '@ossy/platform'
5
+
6
+ export const metadata = {
7
+ id: 'users.select-workspace',
8
+ path: '/api/v0/users/select-workspace',
9
+ method: 'GET',
10
+ query: ['workspaceId', 'redirect'],
11
+ }
12
+
13
+ export default async function handle (req, res) {
14
+ if (req.method !== 'GET') {
15
+ res.setHeader('Allow', 'GET')
16
+ res.status(405).json({ error: 'Method Not Allowed' })
17
+ return
18
+ }
19
+
20
+ if (!req.userId || req.userId === 'anonymous') {
21
+ res.status(401).json({ error: 'Unauthorized' })
22
+ return
23
+ }
24
+
25
+ const workspaceId = req.query.workspaceId
26
+ if (!workspaceId || typeof workspaceId !== 'string') {
27
+ res.status(400).json({ message: 'No workspaceId provided' })
28
+ return
29
+ }
30
+
31
+ try {
32
+ const workspaces = await ActionService.invoke('@ossy/workspaces/actions/list', { req })
33
+ if (!workspaces.some((w) => w.id === workspaceId)) {
34
+ res.status(403).json({ message: 'Forbidden' })
35
+ return
36
+ }
37
+
38
+ mergeUserAppSettingsCookie(req, res, { workspaceId })
39
+
40
+ const redirect = req.query.redirect
41
+ if (redirect && typeof redirect === 'string') {
42
+ res.redirect(302, redirect)
43
+ return
44
+ }
45
+
46
+ res.status(200).json('')
47
+ } catch (err) {
48
+ res.status(err.status || 500).json({ message: err.message || 'Internal Server Error' })
49
+ }
50
+ }
package/src/server.js CHANGED
@@ -10,7 +10,7 @@ import { SDK, resolveActionId } from '@ossy/sdk'
10
10
  import { AggregateRebuild, ensureEventStoreIndexes, Mongo, resolveMongoUrl } from '@ossy/event-store'
11
11
  import { TaskService } from './tasks/task-service.js'
12
12
  import { ChangeStream } from './tasks/change-stream.js'
13
- import { buildManifestSummary } from '@ossy/app/manifest/build-manifest-summary'
13
+ import { buildManifestSummary } from '@ossy/manifest/build-manifest-summary'
14
14
  import { registerSchema } from './resources/schema.registry.js'
15
15
  import { initPlatformSchema } from './resources/schema.service.js'
16
16
  import { IntegrationService } from './integration.service.js'