-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
John
authored and
John
committed
Oct 20, 2023
1 parent
0f5ef64
commit 0fbf78f
Showing
22 changed files
with
1,925 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import express, { Express, Request, Response, NextFunction } from 'express' | ||
import cors from "cors"; | ||
import { resolve } from "path"; | ||
import { unlink, createWriteStream } from "fs"; | ||
const progress = require("request-progress"); | ||
const path = require("path"); | ||
const request = require("request"); | ||
|
||
interface ProgressState { | ||
percent: number; | ||
speed: number; | ||
size: { | ||
total: number; | ||
transferred: number; | ||
}; | ||
time: { | ||
elapsed: number; | ||
remaining: number; | ||
}; | ||
success?: boolean | undefined; | ||
fileName: string; | ||
} | ||
|
||
const options: cors.CorsOptions = { origin: "*" }; | ||
const requiredModules: Record<string, any> = {}; | ||
const port = process.env.PORT || 4000; | ||
const dataDir = __dirname; | ||
type DownloadProgress = Record<string, ProgressState>; | ||
const downloadProgress: DownloadProgress = {}; | ||
const app: Express = express() | ||
app.use(express.static(dataDir + '/renderer')) | ||
app.use(cors(options)) | ||
app.use(express.json()); | ||
|
||
/** | ||
* Execute a plugin module function via API call | ||
* | ||
* @param modulePath path to module name to import | ||
* @param method function name to execute. The methods "deleteFile" and "downloadFile" will call the server function {@link deleteFile}, {@link downloadFile} instead of the plugin function. | ||
* @param args arguments to pass to the function | ||
* @returns Promise<any> | ||
* | ||
*/ | ||
app.post('/api/v1/invokeFunction', (req: Request, res: Response, next: NextFunction): void => { | ||
const method = req.body["method"]; | ||
const args = req.body["args"]; | ||
switch (method) { | ||
case "deleteFile": | ||
deleteFile(args).then(() => res.json(Object())).catch((err: any) => next(err)); | ||
break; | ||
case "downloadFile": | ||
downloadFile(args.downloadUrl, args.fileName).then(() => res.json(Object())).catch((err: any) => next(err)); | ||
break; | ||
default: | ||
const result = invokeFunction(req.body["modulePath"], method, args) | ||
if (typeof result === "undefined") { | ||
res.json(Object()) | ||
} else { | ||
result?.then((result: any) => { | ||
res.json(result) | ||
}).catch((err: any) => next(err)); | ||
} | ||
} | ||
}); | ||
|
||
app.post('/api/v1/downloadProgress', (req: Request, res: Response): void => { | ||
const fileName = req.body["fileName"]; | ||
if (fileName && downloadProgress[fileName]) { | ||
res.json(downloadProgress[fileName]) | ||
return; | ||
} else { | ||
const obj = downloadingFile(); | ||
if (obj) { | ||
res.json(obj) | ||
return; | ||
} | ||
} | ||
res.json(Object()); | ||
}); | ||
|
||
app.use((err: Error, req: Request, res: Response, next: NextFunction): void => { | ||
res.status(500); | ||
res.json({ error: err?.message ?? "Internal Server Error" }) | ||
}); | ||
|
||
app.listen(port, () => console.log(`Application is running on port ${port}`)); | ||
|
||
|
||
async function invokeFunction(modulePath: string, method: string, args: any): Promise<any> { | ||
console.log(modulePath, method, args); | ||
const module = require(/* webpackIgnore: true */ path.join( | ||
dataDir, | ||
"", | ||
modulePath | ||
)); | ||
requiredModules[modulePath] = module; | ||
if (typeof module[method] === "function") { | ||
return module[method](...args); | ||
} else { | ||
return Promise.resolve(); | ||
} | ||
} | ||
|
||
function downloadModel(downloadUrl: string, fileName: string): void { | ||
const userDataPath = process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences' : process.env.HOME + "/.local/share") | ||
|
||
const destination = resolve(userDataPath, fileName); | ||
console.log("Download file", fileName, "to", destination); | ||
progress(request(downloadUrl), {}) | ||
.on("progress", function (state: any) { | ||
downloadProgress[fileName] = { | ||
...state, | ||
fileName, | ||
success: undefined | ||
}; | ||
console.log("downloading file", fileName, (state.percent * 100).toFixed(2) + '%'); | ||
}) | ||
.on("error", function (err: Error) { | ||
downloadProgress[fileName] = { | ||
...downloadProgress[fileName], | ||
success: false, | ||
fileName: fileName, | ||
}; | ||
}) | ||
.on("end", function () { | ||
downloadProgress[fileName] = { | ||
...downloadProgress[fileName], | ||
success: true, | ||
fileName: fileName, | ||
}; | ||
}) | ||
.pipe(createWriteStream(destination)); | ||
} | ||
|
||
function deleteFile(filePath: string): Promise<void> { | ||
const userDataPath = process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences' : process.env.HOME + "/.local/share") | ||
const fullPath = resolve(userDataPath, filePath); | ||
return new Promise((resolve, reject) => { | ||
unlink(fullPath, function (err) { | ||
if (err && err.code === "ENOENT") { | ||
reject(Error(`File does not exist: ${err}`)); | ||
} else if (err) { | ||
reject(Error(`File delete error: ${err}`)); | ||
} else { | ||
console.log(`Delete file ${filePath} from ${fullPath}`) | ||
resolve(); | ||
} | ||
}); | ||
}) | ||
} | ||
|
||
function downloadingFile(): ProgressState | undefined { | ||
const obj = Object.values(downloadProgress).find(obj => obj && typeof obj.success === "undefined") | ||
return obj | ||
} | ||
|
||
|
||
async function downloadFile(downloadUrl: string, fileName: string): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
const obj = downloadingFile(); | ||
if (obj) { | ||
reject(Error(obj.fileName + " is being downloaded!")) | ||
return; | ||
}; | ||
(async () => { | ||
downloadModel(downloadUrl, fileName); | ||
})().catch(e => { | ||
console.error("downloadModel", fileName, e); | ||
}); | ||
resolve(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"watch": [ | ||
"main.ts" | ||
] | ||
} |
Oops, something went wrong.