fix: swagger CSP issue (#1284)
This commit is contained in:
parent
32a82ad565
commit
12b037e2cb
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,6 +11,7 @@ build
|
||||
.DS_Store
|
||||
electron/renderer
|
||||
electron/models
|
||||
electron/docs
|
||||
package-lock.json
|
||||
|
||||
*.log
|
||||
|
||||
18
README.md
18
README.md
@ -148,19 +148,19 @@ Contributions are welcome! Please read the [CONTRIBUTING.md](CONTRIBUTING.md) fi
|
||||
|
||||
1. **Clone the repository and prepare:**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/janhq/jan
|
||||
cd jan
|
||||
git checkout -b DESIRED_BRANCH
|
||||
```
|
||||
```bash
|
||||
git clone https://github.com/janhq/jan
|
||||
cd jan
|
||||
git checkout -b DESIRED_BRANCH
|
||||
```
|
||||
|
||||
2. **Run development and use Jan Desktop**
|
||||
|
||||
```
|
||||
make dev
|
||||
```
|
||||
```bash
|
||||
make dev
|
||||
```
|
||||
|
||||
This will start the development server and open the desktop app.
|
||||
This will start the development server and open the desktop app.
|
||||
|
||||
### For production build
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ export enum AppRoute {
|
||||
relaunch = 'relaunch',
|
||||
joinPath = 'joinPath',
|
||||
baseName = 'baseName',
|
||||
startServer = 'startServer',
|
||||
stopServer = 'stopServer',
|
||||
}
|
||||
|
||||
export enum AppEvent {
|
||||
|
||||
0
electron/docs/openapi/.gitkeep
Normal file
0
electron/docs/openapi/.gitkeep
Normal file
@ -1,9 +1,10 @@
|
||||
import { app, ipcMain, shell, nativeTheme } from 'electron'
|
||||
import { join, basename } from 'path'
|
||||
import { WindowManager } from './../managers/window'
|
||||
import { userSpacePath } from './../utils/path'
|
||||
import { getResourcePath, userSpacePath } from './../utils/path'
|
||||
import { AppRoute } from '@janhq/core'
|
||||
import { ExtensionManager, ModuleManager } from '@janhq/core/node'
|
||||
import { startServer, stopServer } from '@janhq/server'
|
||||
|
||||
export function handleAppIPCs() {
|
||||
/**
|
||||
@ -56,6 +57,23 @@ export function handleAppIPCs() {
|
||||
basename(path)
|
||||
)
|
||||
|
||||
/**
|
||||
* Start Jan API Server.
|
||||
*/
|
||||
ipcMain.handle(AppRoute.startServer, async (_event) =>
|
||||
startServer(
|
||||
app.isPackaged
|
||||
? join(getResourcePath(), 'docs', 'openapi', 'jan.yaml')
|
||||
: undefined,
|
||||
app.isPackaged ? join(getResourcePath(), 'docs', 'openapi') : undefined
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* Stop Jan API Server.
|
||||
*/
|
||||
ipcMain.handle(AppRoute.stopServer, async (_event) => stopServer())
|
||||
|
||||
/**
|
||||
* Relaunches the app in production - reload window in development.
|
||||
* @param _event - The IPC event object.
|
||||
|
||||
@ -20,11 +20,6 @@ import { handleAppUpdates } from './handlers/update'
|
||||
import { handleFsIPCs } from './handlers/fs'
|
||||
import { migrateExtensions } from './utils/migration'
|
||||
|
||||
/**
|
||||
* Server
|
||||
*/
|
||||
import { startServer } from '@janhq/server'
|
||||
|
||||
app
|
||||
.whenReady()
|
||||
.then(createUserSpace)
|
||||
@ -34,7 +29,6 @@ app
|
||||
.then(handleIPCs)
|
||||
.then(handleAppUpdates)
|
||||
.then(createMainWindow)
|
||||
.then(startServer)
|
||||
.then(() => {
|
||||
app.on('activate', () => {
|
||||
if (!BrowserWindow.getAllWindows().length) {
|
||||
|
||||
@ -14,11 +14,13 @@
|
||||
"build/*.{js,map}",
|
||||
"build/**/*.{js,map}",
|
||||
"pre-install",
|
||||
"models/**/*"
|
||||
"models/**/*",
|
||||
"docs/**/*"
|
||||
],
|
||||
"asarUnpack": [
|
||||
"pre-install",
|
||||
"models"
|
||||
"models",
|
||||
"docs"
|
||||
],
|
||||
"publish": [
|
||||
{
|
||||
|
||||
@ -25,7 +25,8 @@
|
||||
"scripts": {
|
||||
"lint": "yarn workspace jan lint && yarn workspace jan-web lint",
|
||||
"test": "yarn workspace jan test:e2e",
|
||||
"dev:electron": "cpx \"models/**\" \"electron/models/\" && yarn workspace jan dev",
|
||||
"copy:assets": "cpx \"models/**\" \"electron/models/\" && cpx \"docs/openapi/**\" \"electron/docs/openapi\"",
|
||||
"dev:electron": "yarn copy:assets && yarn workspace jan dev",
|
||||
"dev:web": "yarn workspace jan-web dev",
|
||||
"dev": "concurrently --kill-others \"yarn dev:web\" \"wait-on http://localhost:3000 && yarn dev:electron\"",
|
||||
"test-local": "yarn lint && yarn build:test && yarn test",
|
||||
@ -34,15 +35,15 @@
|
||||
"build:server": "cd server && yarn install && yarn run build",
|
||||
"build:core": "cd core && yarn install && yarn run build",
|
||||
"build:web": "yarn workspace jan-web build && cpx \"web/out/**\" \"electron/renderer/\"",
|
||||
"build:electron": "cpx \"models/**\" \"electron/models/\" && yarn workspace jan build",
|
||||
"build:electron": "yarn copy:assets && yarn workspace jan build",
|
||||
"build:electron:test": "yarn workspace jan build:test",
|
||||
"build:extensions:windows": "rimraf ./electron/pre-install/*.tgz && powershell -command \"$jobs = Get-ChildItem -Path './extensions' -Directory | ForEach-Object { Start-Job -Name ($_.Name) -ScriptBlock { param($_dir); try { Set-Location $_dir; npm install; npm run build:publish; Write-Output 'Build successful in ' + $_dir } catch { Write-Error 'Error in ' + $_dir; throw } } -ArgumentList $_.FullName }; $jobs | Wait-Job; $jobs | ForEach-Object { Receive-Job -Job $_ -Keep } | ForEach-Object { Write-Host $_ }; $failed = $jobs | Where-Object { $_.State -ne 'Completed' -or $_.ChildJobs[0].JobStateInfo.State -ne 'Completed' }; if ($failed) { Exit 1 }\"",
|
||||
"build:extensions:linux": "rimraf ./electron/pre-install/*.tgz && find ./extensions -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -n 1 -P 4 -I {} sh -c 'cd {} && npm install && npm run build:publish'",
|
||||
"build:extensions:darwin": "rimraf ./electron/pre-install/*.tgz && find ./extensions -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -n 1 -P 4 -I {} sh -c 'cd {} && npm install && npm run build:publish'",
|
||||
"build:extensions": "run-script-os",
|
||||
"build:test": "yarn build:web && yarn workspace jan build:test",
|
||||
"build:test": "yarn copy:assets && yarn build:web && yarn workspace jan build:test",
|
||||
"build": "yarn build:web && yarn build:electron",
|
||||
"build:publish": "cpx \"models/**\" \"electron/models/\" && yarn build:web && yarn workspace jan build:publish"
|
||||
"build:publish": "yarn copy:assets && yarn build:web && yarn workspace jan build:publish"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^8.2.1",
|
||||
|
||||
125
server/index.ts
125
server/index.ts
@ -2,59 +2,92 @@ import fastify from "fastify";
|
||||
import dotenv from "dotenv";
|
||||
import { v1Router } from "@janhq/core/node";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import util from "util";
|
||||
import os from "os";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const JAN_API_HOST = process.env.JAN_API_HOST || "0.0.0.0";
|
||||
const JAN_API_HOST = process.env.JAN_API_HOST || "127.0.0.1";
|
||||
const JAN_API_PORT = Number.parseInt(process.env.JAN_API_PORT || "1337");
|
||||
const serverLogPath = path.join(os.homedir(), "jan", "server.log");
|
||||
|
||||
const server = fastify();
|
||||
server.register(require("@fastify/cors"), {});
|
||||
server.register(require("@fastify/swagger"), {
|
||||
mode: "static",
|
||||
specification: {
|
||||
path: "./../docs/openapi/jan.yaml",
|
||||
baseDir: "./../docs/openapi",
|
||||
},
|
||||
let server: any | undefined = undefined;
|
||||
|
||||
var log_file = fs.createWriteStream(serverLogPath, {
|
||||
flags: "a",
|
||||
});
|
||||
server.register(require("@fastify/swagger-ui"), {
|
||||
routePrefix: "/docs",
|
||||
baseDir: path.join(__dirname, "../..", "./docs/openapi"),
|
||||
uiConfig: {
|
||||
docExpansion: "full",
|
||||
deepLinking: false,
|
||||
},
|
||||
staticCSP: true,
|
||||
transformSpecificationClone: true,
|
||||
});
|
||||
server.register(
|
||||
(childContext, _, done) => {
|
||||
childContext.register(require("@fastify/static"), {
|
||||
root:
|
||||
process.env.EXTENSION_ROOT ||
|
||||
path.join(require("os").homedir(), "jan", "extensions"),
|
||||
wildcard: false,
|
||||
});
|
||||
var log_stdout = process.stdout;
|
||||
var log_stderr = process.stderr;
|
||||
|
||||
done();
|
||||
},
|
||||
{ prefix: "extensions" }
|
||||
);
|
||||
server.register(v1Router, { prefix: "/v1" });
|
||||
|
||||
export const startServer = () => {
|
||||
server
|
||||
.listen({
|
||||
port: JAN_API_PORT,
|
||||
host: JAN_API_HOST,
|
||||
})
|
||||
.then(() => {
|
||||
console.log(
|
||||
`JAN API listening at: http://${JAN_API_HOST}:${JAN_API_PORT}`
|
||||
);
|
||||
});
|
||||
const logServer = function (d: any) {
|
||||
log_file.write(util.format(d) + "\n");
|
||||
log_stdout.write(util.format(d) + "\n");
|
||||
log_stderr.write(util.format(d) + "\n");
|
||||
};
|
||||
|
||||
export const stopServer = () => {
|
||||
server.close();
|
||||
export const startServer = async (schemaPath?: string, baseDir?: string) => {
|
||||
try {
|
||||
server = fastify({
|
||||
logger: {
|
||||
level: "info",
|
||||
file: serverLogPath,
|
||||
},
|
||||
});
|
||||
await server.register(require("@fastify/cors"), {});
|
||||
|
||||
await server.register(require("@fastify/swagger"), {
|
||||
mode: "static",
|
||||
specification: {
|
||||
path: schemaPath ?? "./../docs/openapi/jan.yaml",
|
||||
baseDir: baseDir ?? "./../docs/openapi",
|
||||
},
|
||||
});
|
||||
|
||||
await server.register(require("@fastify/swagger-ui"), {
|
||||
routePrefix: "/docs",
|
||||
baseDir: baseDir ?? path.join(__dirname, "../..", "./docs/openapi"),
|
||||
uiConfig: {
|
||||
docExpansion: "full",
|
||||
deepLinking: false,
|
||||
},
|
||||
staticCSP: false,
|
||||
transformSpecificationClone: true,
|
||||
});
|
||||
|
||||
await server.register(
|
||||
(childContext: any, _: any, done: any) => {
|
||||
childContext.register(require("@fastify/static"), {
|
||||
root:
|
||||
process.env.EXTENSION_ROOT ||
|
||||
path.join(require("os").homedir(), "jan", "extensions"),
|
||||
wildcard: false,
|
||||
});
|
||||
|
||||
done();
|
||||
},
|
||||
{ prefix: "extensions" }
|
||||
);
|
||||
await server.register(v1Router, { prefix: "/v1" });
|
||||
await server
|
||||
.listen({
|
||||
port: JAN_API_PORT,
|
||||
host: JAN_API_HOST,
|
||||
})
|
||||
.then(() => {
|
||||
logServer(
|
||||
`JAN API listening at: http://${JAN_API_HOST}:${JAN_API_PORT}`
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
logServer(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const stopServer = async () => {
|
||||
try {
|
||||
await server.close();
|
||||
} catch (e) {
|
||||
logServer(e);
|
||||
}
|
||||
};
|
||||
|
||||
@ -11,19 +11,23 @@ import {
|
||||
ModalHeader,
|
||||
ModalTitle,
|
||||
ModalTrigger,
|
||||
Badge,
|
||||
} from '@janhq/uikit'
|
||||
|
||||
import { atom, useAtom } from 'jotai'
|
||||
|
||||
import ShortCut from '@/containers/Shortcut'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
|
||||
import { useSettings } from '@/hooks/useSettings'
|
||||
|
||||
const serverEnabledAtom = atom<boolean>(false)
|
||||
|
||||
const Advanced = () => {
|
||||
const { experimentalFeatureEnabed, setExperimentalFeatureEnabled } =
|
||||
useContext(FeatureToggleContext)
|
||||
const [gpuEnabled, setGpuEnabled] = useState<boolean>(false)
|
||||
const [serverEnabled, setServerEnabled] = useAtom(serverEnabledAtom)
|
||||
const { readSettings, saveSettings, validateSettings, setShowNotification } =
|
||||
useSettings()
|
||||
|
||||
@ -87,6 +91,30 @@ const Advanced = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* Server */}
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
<h6 className="text-sm font-semibold capitalize">
|
||||
Enable API Server
|
||||
</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap leading-relaxed">
|
||||
Enable API server for Jan app.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={serverEnabled}
|
||||
onCheckedChange={(e: boolean) => {
|
||||
if (e === true) {
|
||||
window.core?.api?.startServer()
|
||||
} else {
|
||||
window.core?.api?.stopServer()
|
||||
}
|
||||
setServerEnabled(e)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{window.electronAPI && (
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="w-4/5 flex-shrink-0 space-y-1.5">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user