Skip to content

Commit 1f39dfe

Browse files
JohnJohn
authored andcommitted
255: Cloud native
1 parent b9dcdb7 commit 1f39dfe

File tree

23 files changed

+527
-45
lines changed

23 files changed

+527
-45
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
**/node_modules

Dockerfile

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
FROM node:20-bullseye AS base
2+
3+
# 1. Install dependencies only when needed
4+
FROM base AS deps
5+
WORKDIR /app
6+
7+
# Install dependencies based on the preferred package manager
8+
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
9+
RUN yarn install
10+
11+
# # 2. Rebuild the source code only when needed
12+
FROM base AS builder
13+
WORKDIR /app
14+
COPY --from=deps /app/node_modules ./node_modules
15+
COPY . .
16+
# This will do the trick, use the corresponding env file for each environment.
17+
RUN yarn workspace server install
18+
RUN yarn server:prod
19+
20+
# 3. Production image, copy all the files and run next
21+
FROM base AS runner
22+
WORKDIR /app
23+
24+
ENV NODE_ENV=production
25+
26+
# RUN addgroup -g 1001 -S nodejs;
27+
COPY --from=builder /app/server/build ./
28+
29+
# Automatically leverage output traces to reduce image size
30+
# https://nextjs.org/docs/advanced-features/output-file-tracing
31+
COPY --from=builder /app/server/node_modules ./node_modules
32+
COPY --from=builder /app/server/package.json ./package.json
33+
34+
EXPOSE 4000 3928
35+
36+
ENV PORT 4000
37+
ENV APPDATA /app/data
38+
39+
CMD ["node", "main.js"]

electron/core/plugin-manager/execution/facade.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import Plugin from "./Plugin";
99
import { register } from "./activation-manager";
10+
import plugins from "../../../../web/public/plugins/plugin.json"
1011

1112
/**
1213
* @typedef {Object.<string, any>} installOptions The {@link https://www.npmjs.com/package/pacote|pacote options}
@@ -65,7 +66,7 @@ export async function getActive() {
6566
return;
6667
}
6768
// eslint-disable-next-line no-undef
68-
const plgList = await window.pluggableElectronIpc.getActive();
69+
const plgList = await window.pluggableElectronIpc?.getActive() ?? plugins;
6970
return plgList.map(
7071
(plugin) =>
7172
new Plugin(
@@ -90,7 +91,7 @@ export async function registerActive() {
9091
return;
9192
}
9293
// eslint-disable-next-line no-undef
93-
const plgList = await window.pluggableElectronIpc.getActive();
94+
const plgList = await getActive()
9495
plgList.forEach((plugin) =>
9596
register(
9697
new Plugin(

package.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
"workspaces": {
55
"packages": [
66
"electron",
7-
"web"
7+
"web",
8+
"server"
89
],
910
"nohoist": [
1011
"electron",
1112
"electron/**",
1213
"web",
13-
"web/**"
14+
"web/**",
15+
"server",
16+
"server/**"
1417
]
1518
},
1619
"scripts": {
@@ -32,7 +35,10 @@
3235
"build:publish": "yarn build:web && yarn workspace jan build:publish",
3336
"build:publish-darwin": "yarn build:web && yarn workspace jan build:publish-darwin",
3437
"build:publish-win32": "yarn build:web && yarn workspace jan build:publish-win32",
35-
"build:publish-linux": "yarn build:web && yarn workspace jan build:publish-linux"
38+
"build:publish-linux": "yarn build:web && yarn workspace jan build:publish-linux",
39+
"build:web-plugins": "yarn build:web && yarn build:plugins && mkdir -p \"./web/out/plugins/data-plugin\" && cp \"./plugins/data-plugin/dist/esm/index.js\" \"./web/out/plugins/data-plugin\" && mkdir -p \"./web/out/plugins/inference-plugin\" && cp \"./plugins/inference-plugin/dist/index.js\" \"./web/out/plugins/inference-plugin\" && mkdir -p \"./web/out/plugins/model-management-plugin\" && cp \"./plugins/model-management-plugin/dist/index.js\" \"./web/out/plugins/model-management-plugin\" && mkdir -p \"./web/out/plugins/monitoring-plugin\" && cp \"./plugins/monitoring-plugin/dist/index.js\" \"./web/out/plugins/monitoring-plugin\"",
40+
"server:prod": "yarn workspace server build && yarn build:web-plugins && cpx \"web/out/**\" \"server/build/renderer/\" && mkdir -p ./server/build/@janhq && cp -r ./plugins/* ./server/build/@janhq",
41+
"start:server": "yarn server:prod && node server/build/main.js"
3642
},
3743
"devDependencies": {
3844
"concurrently": "^8.2.1",

plugins/data-plugin/module.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const dbs: Record<string, any> = {};
1616
*/
1717
function createCollection(name: string, schema?: { [key: string]: any }): Promise<void> {
1818
return new Promise<void>((resolve) => {
19-
const dbPath = path.join(app.getPath("userData"), "databases");
19+
const dbPath = path.join(appPath(), "databases");
2020
if (!fs.existsSync(dbPath)) fs.mkdirSync(dbPath);
2121
const db = new PouchDB(`${path.join(dbPath, name)}`);
2222
dbs[name] = db;
@@ -226,6 +226,13 @@ function findMany(
226226
.then((data) => data.docs); // Return documents
227227
}
228228

229+
function appPath() {
230+
if (app) {
231+
return app.getPath("userData");
232+
}
233+
return process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences' : process.env.HOME + "/.local/share");
234+
}
235+
229236
module.exports = {
230237
createCollection,
231238
deleteCollection,

plugins/inference-plugin/module.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,22 @@ const initModel = (fileName) => {
2323
let binaryFolder = path.join(__dirname, "nitro"); // Current directory by default
2424
let binaryName;
2525

26-
if (process.platform === "win32") {
27-
// Todo: Need to check for CUDA support to switch between CUDA and non-CUDA binaries
28-
binaryName = "nitro_start_windows.bat";
29-
} else if (process.platform === "darwin") {
30-
// Mac OS platform
31-
binaryName =
32-
process.arch === "arm64"
33-
? "nitro_mac_arm64"
34-
: "nitro_mac_intel";
35-
} else {
36-
// Linux
37-
// Todo: Need to check for CUDA support to switch between CUDA and non-CUDA binaries
38-
binaryName = "nitro_start_linux.sh"; // For other platforms
39-
}
26+
if (process.platform === "win32") {
27+
// Todo: Need to check for CUDA support to switch between CUDA and non-CUDA binaries
28+
binaryName = "nitro_start_windows.bat";
29+
} else if (process.platform === "darwin") {
30+
// Mac OS platform
31+
binaryName = process.arch === "arm64" ? "nitro_mac_arm64" : "nitro_mac_intel";
32+
} else {
33+
// Linux
34+
// Todo: Need to check for CUDA support to switch between CUDA and non-CUDA binaries
35+
binaryName = "nitro_start_linux.sh"; // For other platforms
36+
}
4037

4138
const binaryPath = path.join(binaryFolder, binaryName);
4239

43-
// Execute the binary
44-
subprocess = spawn(binaryPath, { cwd: binaryFolder });
40+
// Execute the binary
41+
subprocess = spawn(binaryPath,["0.0.0.0", PORT], { cwd: binaryFolder });
4542

4643
// Handle subprocess output
4744
subprocess.stdout.on("data", (data) => {
@@ -61,7 +58,7 @@ const initModel = (fileName) => {
6158
})
6259
.then(() => tcpPortUsed.waitUntilUsed(PORT, 300, 30000))
6360
.then(() => {
64-
const llama_model_path = path.join(app.getPath("userData"), fileName);
61+
const llama_model_path = path.join(appPath(), fileName);
6562

6663
const config = {
6764
llama_model_path,
@@ -107,6 +104,13 @@ function killSubprocess() {
107104
}
108105
}
109106

107+
function appPath() {
108+
if (app) {
109+
return app.getPath("userData");
110+
}
111+
return process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences' : process.env.HOME + "/.local/share");
112+
}
113+
110114
module.exports = {
111115
initModel,
112116
killSubprocess,

plugins/inference-plugin/nitro/nitro_start_linux.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
#!/bin/bash
44

55
# Attempt to run the nitro_linux_amd64_cuda file and if it fails, run nitro_linux_amd64
6-
./nitro_linux_amd64_cuda || (echo "nitro_linux_amd64_cuda encountered an error, attempting to run nitro_linux_amd64..." && ./nitro_linux_amd64)
6+
./nitro_linux_amd64_cuda "$@" || (echo "nitro_linux_amd64_cuda encountered an error, attempting to run nitro_linux_amd64..." && ./nitro_linux_amd64 "$@")

plugins/model-management-plugin/index.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,56 @@ import {
55
downloadFile,
66
deleteFile,
77
store,
8+
EventName,
9+
events
810
} from "@janhq/core";
911
import { parseToModel } from "./helper";
1012

11-
const downloadModel = (product) =>
13+
const downloadModel = (product) => {
1214
downloadFile(product.downloadUrl, product.fileName);
15+
checkDownloadProgress(product.fileName);
16+
}
17+
18+
async function checkDownloadProgress(fileName: string) {
19+
if (typeof window !== "undefined" && typeof (window as any).electronAPI === "undefined") {
20+
const intervalId = setInterval(() => {
21+
fetchDownloadProgress(fileName, intervalId);
22+
}, 3000);
23+
}
24+
}
25+
26+
async function fetchDownloadProgress(fileName: string, intervalId: NodeJS.Timeout): Promise<string> {
27+
const response = await fetch("/api/v1/downloadProgress", {
28+
method: 'POST',
29+
body: JSON.stringify({ fileName: fileName }),
30+
headers: { 'Content-Type': 'application/json', 'Authorization': '' }
31+
});
32+
33+
if (!response.ok) {
34+
events.emit(EventName.OnDownloadError, null);
35+
clearInterval(intervalId);
36+
return;
37+
}
38+
const json = await response.json();
39+
if (isEmptyObject(json)) {
40+
if (!fileName && intervalId) {
41+
clearInterval(intervalId);
42+
}
43+
return Promise.resolve("");
44+
}
45+
if (json.success === true) {
46+
events.emit(EventName.OnDownloadSuccess, json);
47+
clearInterval(intervalId);
48+
return Promise.resolve("");
49+
} else {
50+
events.emit(EventName.OnDownloadUpdate, json);
51+
return Promise.resolve(json.fileName);
52+
}
53+
}
54+
55+
function isEmptyObject(ojb: any): boolean {
56+
return Object.keys(ojb).length === 0;
57+
}
1358

1459
const deleteModel = (path) => deleteFile(path);
1560

@@ -87,6 +132,7 @@ function getModelById(modelId: string): Promise<any> {
87132

88133
function onStart() {
89134
store.createCollection("models", {});
135+
fetchDownloadProgress(null, null).then((fileName: string) => fileName && checkDownloadProgress(fileName));
90136
}
91137

92138
// Register all the above functions and objects with the relevant extension points

0 commit comments

Comments
 (0)