@live-change/server 0.1.18 → 0.1.21

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/.idea/misc.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager">
4
+ <output url="file://$PROJECT_DIR$/out" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/server.iml" filepath="$PROJECT_DIR$/server.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
package/lib/Renderer.js CHANGED
@@ -3,6 +3,8 @@ const path = require('path')
3
3
  const serialize = require('serialize-javascript')
4
4
  const renderTemplate = require('./renderTemplate.js')
5
5
 
6
+ const { SitemapStream } = require('sitemap')
7
+
6
8
  class Renderer {
7
9
  constructor(manifest, settings) {
8
10
  this.manifest = manifest
@@ -15,7 +17,9 @@ class Renderer {
15
17
  await this.setupVite()
16
18
  } else {
17
19
  const serverEntryPath = path.resolve(this.root, './dist/server/entry-server.js')
18
- this.renderer = require(serverEntryPath).render
20
+ this.module = require(serverEntryPath)
21
+ this.renderer = this.module.render
22
+ this.sitemap = this.module.sitemap
19
23
  const templatePath = path.resolve(this.root, 'dist/client/index.html')
20
24
  this.template = await fs.promises.readFile(templatePath, { encoding: 'utf-8' })
21
25
  }
@@ -117,6 +121,39 @@ class Renderer {
117
121
  }
118
122
  }
119
123
 
124
+ async getSitemap() {
125
+ if(this.settings.dev) {
126
+ /// Reload every request
127
+ const entryPath = path.resolve(this.root, 'src/entry-server.js')
128
+ return (await this.vite.ssrLoadModule(entryPath)).sitemap
129
+ } else {
130
+ return this.sitemap
131
+ }
132
+ }
133
+
134
+ async renderSitemap({ dao }, res) {
135
+ try {
136
+ res.header('Content-Type', 'application/xml')
137
+ res.status(200)
138
+ const smStream = new SitemapStream({ hostname: (process.env.BASE_HREF || "https://sitemap.com")+'/' })
139
+ smStream.pipe(res)
140
+ const sitemapFunction = await this.getSitemap()
141
+ const { sitemap, router } = await sitemapFunction({ dao })
142
+ function route(location, opts) {
143
+ smStream.write({ url: router.resolve(location).href, changefreq: 'daily', priority: 0.5, ...opts })
144
+ }
145
+ console.log("SR", sitemap, router)
146
+ await sitemap(route)
147
+ //route({ name: 'index' })
148
+ smStream.end()
149
+ } catch(err) {
150
+ console.error("SITEMAP RENDERING ERROR", err)
151
+ res.status(503)
152
+ res.end(`<h4>Internal server error</h4><pre>${err.stack || err.code || err}</pre>`)
153
+ //if(profileOp) await profileLog.end({ ...profileOp, state: 'error', error: err })
154
+ }
155
+ }
156
+
120
157
  fixStackTrace(e) {
121
158
  this.vite && this.vite.ssrFixStacktrace(e)
122
159
  }
package/lib/Services.js CHANGED
@@ -4,6 +4,8 @@ const path = require('path')
4
4
  const resolve = require('util').promisify(require('resolve'))
5
5
  const app = require("@live-change/framework").app()
6
6
 
7
+ const debug = require('debug')('framework')
8
+
7
9
  class Services {
8
10
  constructor(configPath) {
9
11
  if(!configPath) throw new Error("services config parameter is required")
@@ -18,12 +20,12 @@ class Services {
18
20
 
19
21
  async resolve(file) {
20
22
  const path = await resolve(file, { basedir: this.servicesDir })
21
- console.log("PATH RESOLVE", file, "IN", this.servicesDir, "=>", path)
23
+ debug("PATH RESOLVE", file, "IN", this.servicesDir, "=>", path)
22
24
  return path
23
25
  }
24
26
  async getServiceEntryFile(config) {
25
27
  const path = await resolve(config.path, { basedir: this.servicesDir })
26
- console.log("PATH RESOLVE", config.path, "IN", this.servicesDir, "=>", path)
28
+ debug("PATH RESOLVE", config.path, "IN", this.servicesDir, "=>", path)
27
29
  return path
28
30
  }
29
31
 
@@ -39,14 +41,14 @@ class Services {
39
41
  if(this.config.plugins) {
40
42
  for(const plugin of this.config.plugins) {
41
43
  const entryFile = await this.getServiceEntryFile(plugin)
42
- console.log("PLUGIN", plugin, 'ENTRY FILE', entryFile)
44
+ debug("PLUGIN", plugin, 'ENTRY FILE', entryFile)
43
45
  this.plugins.push(require(entryFile))
44
46
  }
45
47
  }
46
48
  if(this.config.services) {
47
49
  for(const service of this.config.services) {
48
50
  const entryFile = await this.getServiceEntryFile(service)
49
- console.log("SERVICE", service, 'ENTRY FILE', entryFile)
51
+ debug("SERVICE", service, 'ENTRY FILE', entryFile)
50
52
  const definition = require(entryFile)
51
53
  if(definition.name != service.name) {
52
54
  console.error("SERVICE", service, "NAME", service.name, "MISMATCH", definition.name)
package/lib/SsrServer.js CHANGED
@@ -21,7 +21,7 @@ class SsrServer {
21
21
 
22
22
  this.instanceId = encodeNumber(hashCode(
23
23
  `ssr${process.pid}${require("os").hostname()} ${process.cwd()}/${process.argv.join(' ')}`))
24
- this.uidGenerator = uidGenerator(this.instanceId, 1)
24
+ this.uidGenerator = uidGenerator(this.instanceId, 1, this.settings.uidBorders)
25
25
 
26
26
  this.root = this.settings.root || process.cwd()
27
27
  }
@@ -54,6 +54,23 @@ class SsrServer {
54
54
  await this.setupSsr()
55
55
  }
56
56
 
57
+ async createDao(credentials, clientIp) {
58
+ let dao
59
+ if(this.settings.daoFactory) {
60
+ dao = await this.settings.daoFactory(credentials, clientIp)
61
+ } else {
62
+ const host = (this.settings.apiHost == '0.0.0.0' || !this.settings.apiHost)
63
+ ? 'localhost' : this.settings.apiHost
64
+ dao = await serverDao(credentials, clientIp, {
65
+ remoteUrl: `ws://${host}:${this.settings.apiPort || 8002}/api/ws`
66
+ })
67
+ }
68
+ if(!dao) {
69
+ throw new Error("Impossible to render page without data access object. Define apiServer or daoFactory!")
70
+ }
71
+ return dao
72
+ }
73
+
57
74
  async setupSsr() {
58
75
  const readCredentials = this.settings.readCredentials || ((req) => {
59
76
  const cookies = cookie.parse(req.headers.cookie || '')
@@ -70,6 +87,13 @@ class SsrServer {
70
87
  })
71
88
  }
72
89
  })
90
+
91
+ this.express.get('/sitemap.xml', async (req, res) => {
92
+ const sitemap = await this.renderer.getSitemap()
93
+ const clientIp = getIp(req)
94
+ const dao = await this.createDao({ sessionKey: 'sitemap' })
95
+ this.renderer.renderSitemap({ dao, clientIp }, res)
96
+ })
73
97
  this.express.use('*', async (req, res) => {
74
98
  const url = req.originalUrl
75
99
  const clientIp = getIp(req)
@@ -77,33 +101,35 @@ class SsrServer {
77
101
  const credentials = readCredentials(req)
78
102
  const windowId = this.uidGenerator()
79
103
  try {
80
- let dao
81
- if(this.settings.daoFactory) {
82
- dao = await this.settings.daoFactory(credentials, clientIp)
83
- } else {
84
- const host = (this.settings.apiHost == '0.0.0.0' || !this.settings.apiHost)
85
- ? 'localhost' : this.settings.apiHost
86
- dao = await serverDao(credentials, clientIp, {
87
- remoteUrl: `ws://${host}:${this.settings.apiPort || 8002}/api/ws`
88
- })
89
- }
90
- if(!dao)
91
- throw new Error("Impossible to render page without data access object. Define apiServer or daoFactory!")
92
-
104
+ const dao = await this.createDao(credentials, clientIp)
93
105
  const version = this.version
94
106
 
95
- const html = await this.renderer.renderPage({ url, dao, clientIp, credentials, windowId, version })
107
+ let html
108
+ let error
96
109
 
97
- res.status(200)
98
- writeCredentials(res, credentials)
99
- res.set({
100
- 'Content-Type': 'text/html'
101
- })
102
- res.end(html)
110
+ for(let retry = 0; retry < 3; retry ++) {
111
+ try {
112
+ html = await this.renderer.renderPage({ url, dao, clientIp, credentials, windowId, version })
113
+ break
114
+ } catch(e) {
115
+ error = e
116
+ }
117
+ }
118
+ if(html) {
119
+ res.status(200)
120
+ writeCredentials(res, credentials)
121
+ res.set({
122
+ 'Content-Type': 'text/html'
123
+ })
124
+ res.end(html)
125
+ } else {
126
+ if(error.stack) this.renderer.fixStackTrace(error)
127
+ console.error("RENDERING ERROR", error.stack || error)
128
+ res.status(500).end(error.stack || error)
129
+ }
103
130
  } catch (e) {
104
- this.renderer.fixStackTrace(e)
105
- console.error("RENDERING ERROR", e.stack || e)
106
- res.status(500).end(e.stack)
131
+ console.error("ERROR", e.stack || e)
132
+ res.status(500).end(e.stack || e)
107
133
  }
108
134
  })
109
135
  }
package/lib/TestServer.js CHANGED
@@ -27,7 +27,7 @@ class TestServer {
27
27
 
28
28
  app.instanceId = encodeNumber(hashCode(
29
29
  `app${process.pid}${require("os").hostname()} ${process.cwd()}/${process.argv.join(' ')}`))
30
- app.uidGenerator = uidGenerator(app.instanceId, 1)
30
+ app.uidGenerator = uidGenerator(app.instanceId, 1, '[]')
31
31
  this.dbServer = await setupDbServer({ dbBackend: 'mem' })
32
32
  const loopbackDao = await createLoopbackDao('local', () => this.dbServer.createDao('local'))
33
33
  app.dao = loopbackDao
@@ -60,8 +60,9 @@ class TestServer {
60
60
 
61
61
  await new Promise((resolve, reject) => {
62
62
  this.httpServer = this.expressServer.listen(this.config.port || 0, () => {
63
- this.port = this.expressServer.address().port,
63
+ this.port = this.expressServer.address().port
64
64
  this.url = `http://localhost:${this.expressServer.address().port}`
65
+ process.env.SSR_PORT = this.port
65
66
  resolve()
66
67
  })
67
68
  })
@@ -39,6 +39,19 @@ async function setupApiServer(settings) {
39
39
  }
40
40
  })
41
41
  }
42
+ if(settings.dbAccess) {
43
+ local.serverDatabase = {
44
+ observable(what) {
45
+ return app.dao.observable(['database', ...what.slice(1)])
46
+ },
47
+ get(what) {
48
+ return app.dao.get(['database', ...what.slice(1)])
49
+ },
50
+ request(what, ...args) {
51
+ return app.dao.request(['database', ...what.slice(1)], ...args)
52
+ }
53
+ }
54
+ }
42
55
  return local
43
56
  },
44
57
  shareDefinition: true,
package/lib/setupApp.js CHANGED
@@ -4,12 +4,14 @@ const setupDbServer = require('./setupDbServer.js')
4
4
  const setupDbClient = require('./setupDbClient.js')
5
5
  const createLoopbackDao = require('./createLoopbackDao.js')
6
6
 
7
+ const debug = require('debug')('server:app')
8
+
7
9
  async function setupApp(settings, env = process.env) {
8
10
  const app = require("@live-change/framework").app()
9
11
  app.instanceId = encodeNumber(hashCode(
10
12
  `app${process.pid}${require("os").hostname()} ${process.cwd()}/${process.argv.join(' ')}`))
11
- app.uidGenerator = uidGenerator(app.instanceId, 1)
12
- console.log("SETUP APP", settings)
13
+ app.uidGenerator = uidGenerator(app.instanceId, 1, settings.uidBorders)
14
+ debug("SETUP APP", settings)
13
15
  let dbServer
14
16
  if(settings.withDb) {
15
17
  dbServer = await setupDbServer(settings)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/server",
3
- "version": "0.1.18",
3
+ "version": "0.1.21",
4
4
  "description": "Live Change Framework - server",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -23,10 +23,10 @@
23
23
  "dependencies": {
24
24
  "@live-change/dao": "^0.3.12",
25
25
  "@live-change/dao-sockjs": "^0.2.1",
26
- "@live-change/db-server": "^0.4.82",
26
+ "@live-change/db-server": "^0.5.3",
27
27
  "@live-change/framework": "^0.5.10",
28
28
  "@live-change/vue3-ssr": "^0.1.7",
29
- "@live-change/uid": "^0.1.3",
29
+ "@live-change/uid": "^0.1.4",
30
30
  "http-proxy-middleware": "2.0.1",
31
31
  "dotenv": "^10.0.0",
32
32
  "express": "^4.17.1",
@@ -36,6 +36,7 @@
36
36
  "@live-change/sockjs": "^0.4.0-rc.1",
37
37
  "websocket": "^1.0.34",
38
38
  "yargs": "^17.3.0",
39
- "express-static-gzip": "2.1.1"
39
+ "express-static-gzip": "2.1.1",
40
+ "sitemap": "^6.3.6"
40
41
  }
41
42
  }
package/server.iml ADDED
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
4
+ <exclude-output />
5
+ <content url="file://$MODULE_DIR$" />
6
+ <orderEntry type="inheritedJdk" />
7
+ <orderEntry type="sourceFolder" forTests="false" />
8
+ </component>
9
+ </module>