1
1
mirror of https://github.com/leon-ai/leon.git synced 2024-12-24 01:04:28 +03:00

feat(server): generate Fastify routes on the file to expose packages over HTTP

This commit is contained in:
louistiti 2021-12-30 23:06:30 +08:00
parent f1f48757e2
commit 5b41713a68
No known key found for this signature in database
GPG Key ID: 7ECA3DD523793FE6
7 changed files with 255 additions and 3 deletions

View File

@ -24,6 +24,9 @@ LEON_TTS=false
# Text-to-speech provider
LEON_TTS_PROVIDER=flite
# Enable/disable packages to be available over HTTP
PACKAGES_OVER_HTTP=true
# Enable/disable collaborative logger
LEON_LOGGER=true

132
core/pkgs-endpoints.json Normal file
View File

@ -0,0 +1,132 @@
{
"endpoints": [
{
"method": "POST",
"route": "/p/calendar/todolist/create_list",
"params": [
"list"
]
},
{
"method": "GET",
"route": "/p/calendar/todolist/view_lists",
"params": []
},
{
"method": "POST",
"route": "/p/calendar/todolist/view_list",
"params": [
"list"
]
},
{
"method": "POST",
"route": "/p/calendar/todolist/rename_list",
"params": [
"old_list",
"new_list"
]
},
{
"method": "POST",
"route": "/p/calendar/todolist/delete_list",
"params": [
"list"
]
},
{
"method": "POST",
"route": "/p/calendar/todolist/add_todos",
"params": [
"todos",
"list"
]
},
{
"method": "POST",
"route": "/p/calendar/todolist/complete_todos",
"params": [
"todos",
"list"
]
},
{
"method": "POST",
"route": "/p/calendar/todolist/uncheck_todos",
"params": [
"todos",
"list"
]
},
{
"method": "GET",
"route": "/p/checker/isitdown/run",
"params": []
},
{
"method": "GET",
"route": "/p/checker/haveibeenpwned/run",
"params": []
},
{
"method": "GET",
"route": "/p/leon/whoami/run",
"params": []
},
{
"method": "GET",
"route": "/p/leon/joke/run",
"params": []
},
{
"method": "GET",
"route": "/p/leon/greeting/run",
"params": []
},
{
"method": "GET",
"route": "/p/leon/welcome/run",
"params": []
},
{
"method": "GET",
"route": "/p/leon/meaningoflife/run",
"params": []
},
{
"method": "GET",
"route": "/p/leon/randomnumber/run",
"params": []
},
{
"method": "GET",
"route": "/p/leon/bye/run",
"params": []
},
{
"method": "GET",
"route": "/p/leon/partnerassistant/run",
"params": []
},
{
"method": "GET",
"route": "/p/network/speedtest/run",
"params": []
},
{
"method": "GET",
"route": "/p/trend/github/run",
"params": []
},
{
"method": "GET",
"route": "/p/trend/producthunt/run",
"params": []
},
{
"method": "GET",
"route": "/p/videodownloader/youtube/run",
"params": []
}
]
}

View File

@ -36,13 +36,14 @@
"preinstall": "node scripts/setup/preinstall.js",
"postinstall": "babel-node scripts/setup/setup.js",
"dev:app": "vite --config app/vite.config.js",
"dev:server": "npm run train && cross-env LEON_NODE_ENV=development nodemon --watch server ./server/src/index.js --ignore server/src/tmp/ --exec babel-node",
"dev:server": "npm run train && npm run generate:pkgs-endpoints && cross-env LEON_NODE_ENV=development nodemon --watch server ./server/src/index.js --ignore server/src/tmp/ --exec babel-node",
"wake": "cross-env LEON_HOST=http://localhost LEON_PORT=1337 node hotword/index.js",
"delete-dist:server": "shx rm -rf ./server/dist",
"prepare": "husky install",
"generate:pkgs-endpoints": "babel-node scripts/run-generate-pkgs-endpoints.js",
"build": "npm run build:app && npm run build:server",
"build:app": "cross-env LEON_NODE_ENV=production babel-node scripts/app/run-build-app.js",
"build:server": "npm run delete-dist:server && npm run train && babel ./server/src -d ./server/dist --copy-files && shx mkdir -p server/dist/tmp",
"build:server": "npm run delete-dist:server && npm run train && npm run generate:pkgs-endpoints && babel ./server/src -d ./server/dist --copy-files && shx mkdir -p server/dist/tmp",
"start": "cross-env LEON_NODE_ENV=production node ./server/dist/index.js",
"train": "babel-node scripts/run-train.js",
"prepare-release": "babel-node scripts/release/prepare-release.js",

View File

@ -0,0 +1,87 @@
import dotenv from 'dotenv'
import fs from 'fs'
import path from 'path'
import log from '@/helpers/log'
import { langs } from '@@/core/langs.json'
dotenv.config()
/**
* Generate packages endpoints script
* Parse and convert packages configuration into a JSON file understandable by Fastify
* to dynamically generate endpoints so packages can be accessible over HTTP
*/
export default () => new Promise(async (resolve, reject) => {
const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
const packagesDir = 'packages'
const outputFile = '/core/pkgs-endpoints.json'
const lang = langs[process.env.LEON_LANG].short.toLowerCase().substr(0, 2)
try {
// TODO: check packages folder mtime first
log.info('Parsing packages configuration...')
const packages = fs.readdirSync(packagesDir)
.filter((entity) => fs.statSync(path.join(packagesDir, entity)).isDirectory())
const finalObj = {
endpoints: []
}
let pkgObj = { }
for (let i = 0; i < packages.length; i += 1) {
const pkg = packages[i]
pkgObj = JSON.parse(fs.readFileSync(`${packagesDir}/${pkg}/data/expressions/${lang}.json`, 'utf8'))
const modules = Object.keys(pkgObj)
for (let j = 0; j < modules.length; j += 1) {
const module = modules[j]
const actions = Object.keys(pkgObj[module])
for (let k = 0; k < actions.length; k += 1) {
const action = actions[k]
const actionObj = pkgObj[module][action]
const { entities, http_api } = actionObj // eslint-disable-line camelcase
let finalMethod = entities ? 'POST' : 'GET'
if (http_api?.method) {
finalMethod = http_api.method.toUpperCase()
}
if (!supportedMethods.includes(finalMethod)) {
reject(`The "${finalMethod}" HTTP method of the ${pkg}/${module}/${action} action is not supported`)
}
const endpoint = {
method: finalMethod.toUpperCase(),
route: `/p/${pkg}/${module}/${action}`,
params: []
}
if (http_api?.timeout) {
endpoint.timeout = http_api.timeout
}
if (entities) {
endpoint.params = entities.map((entity) => entity.name)
}
finalObj.endpoints.push(endpoint)
}
}
}
log.info(`Writing ${outputFile} file...`)
try {
fs.writeFileSync(path.join(__dirname, `..${outputFile}`), JSON.stringify(finalObj, null, 2))
log.success(`${outputFile} file generated`)
resolve()
} catch (e) {
reject(`Failed to generate ${outputFile} file: ${e.message}`)
}
} catch (e) {
log.error(e.message)
reject(e)
}
})

View File

@ -0,0 +1,14 @@
import log from '@/helpers/log'
import generatePkgsEndpoints from './generate-pkgs-endpoints'
/**
* Execute the generating packages endpoints script
*/
(async () => {
try {
await generatePkgsEndpoints()
} catch (e) {
log.error(`Failed to generate packages endpoints: ${e}`)
}
})()

View File

@ -6,7 +6,7 @@ import path from 'path'
import log from '@/helpers/log'
import string from '@/helpers/string'
import { langs } from '../core/langs.json'
import { langs } from '@@/core/langs.json'
dotenv.config()

View File

@ -5,6 +5,7 @@ import { join } from 'path'
import { version } from '@@/package.json'
import { langs } from '@@/core/langs.json'
import { endpoints } from '@@/core/pkgs-endpoints.json'
import Nlu from '@/core/nlu'
import Brain from '@/core/brain'
import Asr from '@/core/asr'
@ -69,6 +70,20 @@ class Server {
this.fastify.register(infoPlugin, { apiVersion })
this.fastify.register(downloadsPlugin, { apiVersion })
if (process.env.PACKAGES_OVER_HTTP === 'true') {
endpoints.forEach((endpoint) => {
this.fastify.route({
method: endpoint.method,
url: endpoint.route,
handler: (request, reply) => {
// TODO: get params request.body... endpoint.params...
// TODO: this.brain.execute(obj)
reply.send({ hello: 'world' })
}
})
})
}
this.httpServer = this.fastify.server
try {