@ossy/platform 1.13.0 → 1.14.1

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 (2) hide show
  1. package/package.json +4 -4
  2. package/src/server.js +71 -70
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/platform",
3
- "version": "1.13.0",
3
+ "version": "1.14.1",
4
4
  "description": "Ossy application server runtime",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,8 +22,8 @@
22
22
  "author": "Ossy <yourfriends@ossy.se> (https://ossy.se)",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@ossy/router": "^1.14.0",
26
- "@ossy/sdk": "^1.14.0",
25
+ "@ossy/router": "^1.15.1",
26
+ "@ossy/sdk": "^1.15.1",
27
27
  "cookie-parser": "^1.4.7",
28
28
  "express": ">=5.0.0 <6.0.0",
29
29
  "morgan": ">=1.10.1 <2.0.0",
@@ -33,5 +33,5 @@
33
33
  "files": [
34
34
  "src"
35
35
  ],
36
- "gitHead": "c496b1f449dba609d65efac16aa100a02a5f0595"
36
+ "gitHead": "6476ba57e0bdf65f55dfd6671a59c7a7ad773327"
37
37
  }
package/src/server.js CHANGED
@@ -1,70 +1,53 @@
1
1
  import fs from 'node:fs'
2
2
  import path from 'path'
3
- import url from 'url'
4
- import { pathToFileURL } from 'node:url'
3
+ import { fileURLToPath, pathToFileURL } from 'node:url'
5
4
  import express from 'express'
6
5
  import morgan from 'morgan'
7
6
  import { Router as OssyRouter } from '@ossy/router'
8
7
  import cookieParser from 'cookie-parser'
9
8
  import { ProxyInternal } from './proxy-internal.js'
10
9
 
11
- import buildTimeConfig from './.ossy/server-config.runtime.mjs'
12
- import { BuildPage, buildPrerenderAppConfig } from './.ossy/render-page.task.js'
13
- import Middleware from './.ossy/middleware.runtime.js'
14
-
15
- const __ossyDir = path.dirname(url.fileURLToPath(import.meta.url)) + '/.ossy'
16
-
17
- function readOssyJson (name) {
18
- return JSON.parse(fs.readFileSync(path.join(__ossyDir, name), 'utf8'))
19
- }
20
-
21
- const apiRouteList = readOssyJson('api.generated.json') ?? []
22
- const sitePageList = readOssyJson('pages.generated.json') ?? []
23
-
24
- function pageRouterLanguageOptions (config, pages) {
25
- let supported = config?.supportedLanguages
26
- let defaultLanguage = config?.defaultLanguage
27
- if ((!supported || supported.length <= 1) && pages.length > 0) {
28
- const p0 = pages[0]
29
- if (p0 && typeof p0.path === 'object' && p0.path != null) {
30
- supported = Object.keys(p0.path)
31
- defaultLanguage = defaultLanguage || supported[0]
32
- }
33
- }
34
- return {
35
- supportedLanguages: Array.isArray(supported) ? supported : [],
36
- defaultLanguage,
10
+ const currentDir = path.dirname(fileURLToPath(import.meta.url))
11
+ const ossyDir = path.join(currentDir, '.ossy')
12
+ const readJson = (name, fallback) => {
13
+ try {
14
+ return JSON.parse(fs.readFileSync(path.join(ossyDir, name), 'utf8'))
15
+ } catch {
16
+ return fallback
37
17
  }
38
18
  }
39
19
 
40
- const app = express()
41
-
42
- const currentDir = path.dirname(url.fileURLToPath(import.meta.url))
43
- const ROOT_PATH = path.resolve(currentDir, 'public')
44
-
45
- function parsePortFromArgv (argv) {
46
- const idx = argv.findIndex((a) => a === '--port' || a === '-p')
47
- if (idx !== -1 && argv[idx + 1]) return argv[idx + 1]
48
-
49
- const eq = argv.find((a) => a.startsWith('--port='))
50
- if (eq) return eq.split('=')[1]
51
-
52
- return undefined
20
+ const loadDefaultFromRef = async (runtimeRefJson) => {
21
+ const modulePath = readJson(runtimeRefJson, null)?.modulePath
22
+ if (!modulePath || typeof modulePath !== 'string') return undefined
23
+ try {
24
+ const mod = await import(pathToFileURL(path.resolve(modulePath)).href)
25
+ return mod?.default
26
+ } catch {
27
+ return undefined
28
+ }
53
29
  }
54
30
 
55
- function normalizePort (value, fallback) {
56
- if (value === undefined || value === null || value === '') return fallback
57
- const n = Number.parseInt(String(value), 10)
58
- if (!Number.isFinite(n) || n <= 0) return fallback
59
- return n
31
+ const apiRouteList = readJson('api.generated.json', [])
32
+ const sitePageList = readJson('pages.generated.json', [])
33
+ const buildTimeConfig = (await loadDefaultFromRef('server-config.runtime.json')) || {}
34
+ const Middleware = (await loadDefaultFromRef('middleware.runtime.json')) || []
35
+ let ssrRender = null
36
+ try {
37
+ ssrRender = (await import(pathToFileURL(path.resolve(currentDir, 'ssr', 'app.mjs')).href)).renderPage
38
+ } catch {
39
+ // SSR is optional at startup; page requests return 503 when unavailable.
60
40
  }
61
41
 
62
- const DEFAULT_PORT = 3000
63
- const port = normalizePort(parsePortFromArgv(process.argv) ?? process.env.PORT, DEFAULT_PORT)
64
-
65
- if (Middleware !== undefined) {
66
- console.log(`[@ossy/platform][server] ${Middleware?.length || 0} custom middleware loaded`)
67
- }
42
+ const app = express()
43
+ const ROOT_PATH = path.resolve(currentDir, 'public')
44
+ const explicitPortArgIdx = process.argv.findIndex((a) => a === '--port' || a === '-p')
45
+ const explicitPortArg =
46
+ explicitPortArgIdx !== -1
47
+ ? process.argv[explicitPortArgIdx + 1]
48
+ : process.argv.find((a) => a.startsWith('--port='))?.split('=')[1]
49
+ const parsedPort = Number.parseInt(String(explicitPortArg ?? process.env.PORT ?? '3000'), 10)
50
+ const port = Number.isFinite(parsedPort) && parsedPort > 0 ? parsedPort : 3000
68
51
 
69
52
  const middleware = [
70
53
  morgan('tiny'),
@@ -80,26 +63,28 @@ const middleware = [
80
63
  req.isAuthenticated = cookieHeader ? cookieHeader.includes('auth=') : false
81
64
  next()
82
65
  },
83
- ...(Middleware || []),
66
+ ...(Array.isArray(Middleware) ? Middleware : []),
84
67
  express.static(ROOT_PATH),
85
68
  ProxyInternal(),
86
69
  ]
87
70
 
88
71
  app.use(middleware)
89
72
 
90
- const apiRouter = OssyRouter.of({
91
- pages: apiRouteList,
92
- })
93
-
94
- const { supportedLanguages, defaultLanguage } = pageRouterLanguageOptions(
95
- buildTimeConfig,
96
- sitePageList
97
- )
73
+ let supportedLanguages = buildTimeConfig?.supportedLanguages
74
+ let defaultLanguage = buildTimeConfig?.defaultLanguage
75
+ if ((!supportedLanguages || supportedLanguages.length <= 1) && sitePageList.length > 0) {
76
+ const firstPage = sitePageList[0]
77
+ if (firstPage && typeof firstPage.path === 'object' && firstPage.path != null) {
78
+ supportedLanguages = Object.keys(firstPage.path)
79
+ defaultLanguage = defaultLanguage || supportedLanguages[0]
80
+ }
81
+ }
98
82
 
83
+ const apiRouter = OssyRouter.of({ pages: apiRouteList })
99
84
  const pageRouter = OssyRouter.of({
100
85
  pages: sitePageList,
86
+ supportedLanguages: Array.isArray(supportedLanguages) ? supportedLanguages : [],
101
87
  defaultLanguage,
102
- supportedLanguages,
103
88
  })
104
89
 
105
90
  app.all('*all', async (req, res) => {
@@ -107,7 +92,7 @@ app.all('*all', async (req, res) => {
107
92
  try {
108
93
  const apiRoute = apiRouter.getPageByUrl(requestUrl)
109
94
  if (apiRoute?.module) {
110
- const mod = await import(pathToFileURL(path.resolve(__ossyDir, apiRoute.module)).href)
95
+ const mod = await import(pathToFileURL(path.resolve(ossyDir, apiRoute.module)).href)
111
96
  await mod.default(req, res)
112
97
  return
113
98
  }
@@ -117,14 +102,30 @@ app.all('*all', async (req, res) => {
117
102
  }
118
103
  const pageRoute = pageRouter.getPageByUrl(requestUrl)
119
104
  if (pageRoute) {
120
- const appConfig = buildPrerenderAppConfig({
121
- buildTimeConfig,
122
- pageList: sitePageList,
123
- activeRouteId: pageRoute.id,
124
- urlPath: requestUrl,
105
+ if (typeof ssrRender !== 'function') {
106
+ res.status(503).type('text').send('SSR runtime unavailable')
107
+ return
108
+ }
109
+ const appConfig = {
110
+ ...buildTimeConfig,
111
+ url: requestUrl,
112
+ theme: buildTimeConfig?.theme || 'light',
125
113
  isAuthenticated: !!req.isAuthenticated,
126
- })
127
- const html = await BuildPage.handle({ route: pageRoute, appConfig })
114
+ workspaceId: buildTimeConfig?.workspaceId,
115
+ apiUrl: buildTimeConfig?.apiUrl,
116
+ pages: sitePageList.map((page) => ({ id: page?.id, path: page?.path })),
117
+ pageId: pageRoute.id,
118
+ sidebarPrimaryCollapsed: false,
119
+ }
120
+ const html = await ssrRender(pageRoute.id, {
121
+ ...appConfig,
122
+ theme: appConfig?.theme == null ? appConfig?.theme : JSON.parse(JSON.stringify(appConfig.theme)),
123
+ themes: appConfig?.themes == null ? appConfig?.themes : JSON.parse(JSON.stringify(appConfig.themes)),
124
+ resourceTemplates:
125
+ appConfig?.resourceTemplates == null
126
+ ? appConfig?.resourceTemplates
127
+ : JSON.parse(JSON.stringify(appConfig.resourceTemplates)),
128
+ }, {})
128
129
  res.status(200).type('html').send(html)
129
130
  return
130
131
  }