@fett/synology-api 0.0.1-beta.9 → 0.0.1

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.
Files changed (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -10
  3. package/lib/cli/apis.js +47 -0
  4. package/lib/{esm/cli → cli}/config.js +38 -41
  5. package/lib/cli/core.js +20 -0
  6. package/lib/{types/cli → cli}/helper.d.ts +1 -0
  7. package/lib/{esm/cli → cli}/helper.js +9 -5
  8. package/lib/cli/index.d.ts +2 -0
  9. package/lib/cli/index.js +5 -0
  10. package/lib/{types/core.d.ts → core.d.ts} +1 -1
  11. package/lib/core.js +99 -0
  12. package/lib/decorators/bindMethods.js +45 -0
  13. package/lib/{types/errorcodes.d.ts → errorcodes.d.ts} +2 -2
  14. package/lib/{esm/errorcodes.js → errorcodes.js} +10 -5
  15. package/lib/helpers.js +70 -0
  16. package/lib/index.d.ts +4 -0
  17. package/lib/index.js +4 -0
  18. package/lib/{types/modules → modules}/Api/Auth.d.ts +2 -2
  19. package/lib/modules/Api/Auth.js +30 -0
  20. package/lib/modules/Api/Info.d.ts +3 -0
  21. package/lib/modules/Api/Info.js +15 -0
  22. package/lib/modules/Api/index.d.ts +2 -0
  23. package/lib/modules/Api/index.js +2 -0
  24. package/lib/modules/AudioStation/Album.d.ts +30 -0
  25. package/lib/modules/AudioStation/Album.js +17 -0
  26. package/lib/modules/AudioStation/Artist.d.ts +25 -0
  27. package/lib/modules/AudioStation/Artist.js +17 -0
  28. package/lib/modules/AudioStation/Cover.js +0 -0
  29. package/lib/modules/AudioStation/Folder.d.ts +24 -0
  30. package/lib/modules/AudioStation/Folder.js +17 -0
  31. package/lib/modules/AudioStation/Info.d.ts +45 -0
  32. package/lib/modules/AudioStation/Info.js +9 -0
  33. package/lib/modules/AudioStation/Lyrics.d.ts +25 -0
  34. package/lib/modules/AudioStation/Lyrics.js +23 -0
  35. package/lib/modules/AudioStation/Playlist.d.ts +34 -0
  36. package/lib/modules/AudioStation/Playlist.js +38 -0
  37. package/lib/modules/AudioStation/Search.d.ts +53 -0
  38. package/lib/modules/AudioStation/Search.js +17 -0
  39. package/lib/modules/AudioStation/Song.d.ts +33 -0
  40. package/lib/modules/AudioStation/Song.js +27 -0
  41. package/lib/modules/AudioStation/index.d.ts +23 -0
  42. package/lib/modules/AudioStation/index.js +23 -0
  43. package/lib/modules/Auth/AuthKey.d.ts +8 -0
  44. package/lib/modules/Auth/AuthKey.js +12 -0
  45. package/lib/modules/Auth/index.d.ts +7 -0
  46. package/lib/modules/Auth/index.js +8 -0
  47. package/lib/modules/FileStation/File.d.ts +0 -0
  48. package/lib/modules/FileStation/File.js +0 -0
  49. package/lib/{types/modules → modules}/FileStation/Info.d.ts +1 -1
  50. package/lib/modules/FileStation/Info.js +9 -0
  51. package/lib/{types/modules → modules}/FileStation/List.d.ts +1 -1
  52. package/lib/modules/FileStation/List.js +30 -0
  53. package/lib/{types/modules → modules}/FileStation/index.d.ts +2 -2
  54. package/lib/{esm/modules → modules}/FileStation/index.js +2 -2
  55. package/lib/modules/VideoStation/File.d.ts +48 -0
  56. package/lib/modules/VideoStation/File.js +20 -0
  57. package/lib/modules/VideoStation/HomeVideo.d.ts +6 -0
  58. package/lib/modules/VideoStation/HomeVideo.js +10 -0
  59. package/lib/modules/VideoStation/Info.d.ts +44 -0
  60. package/lib/modules/VideoStation/Info.js +9 -0
  61. package/lib/modules/VideoStation/Library.d.ts +70 -0
  62. package/lib/modules/VideoStation/Library.js +23 -0
  63. package/lib/modules/VideoStation/Movie.d.ts +27 -0
  64. package/lib/modules/VideoStation/Movie.js +13 -0
  65. package/lib/modules/VideoStation/Streaming.d.ts +11 -0
  66. package/lib/modules/VideoStation/Streaming.js +24 -0
  67. package/lib/modules/VideoStation/index.d.ts +15 -0
  68. package/lib/modules/VideoStation/index.js +16 -0
  69. package/lib/modules/VideoStation/types.d.ts +27 -0
  70. package/lib/modules/VideoStation/types.js +1 -0
  71. package/lib/modules/index.d.ts +26 -0
  72. package/lib/{esm/modules → modules}/index.js +31 -3
  73. package/lib/types/{types/apiInfo.d.ts → apiInfo.d.ts} +15 -2
  74. package/lib/{esm/types → types}/apiInfo.js +15 -1
  75. package/lib/types/index.d.ts +2 -5
  76. package/lib/types/index.js +2 -0
  77. package/lib/{types/utils.d.ts → utils.d.ts} +1 -0
  78. package/lib/{esm/utils.js → utils.js} +3 -0
  79. package/package.json +23 -24
  80. package/bin/syno +0 -6
  81. package/lib/cjs/index.js +0 -728
  82. package/lib/esm/cli/apis.js +0 -53
  83. package/lib/esm/cli/index.js +0 -30
  84. package/lib/esm/core.js +0 -111
  85. package/lib/esm/helpers.js +0 -81
  86. package/lib/esm/index.js +0 -5
  87. package/lib/esm/modules/Api/Auth.js +0 -43
  88. package/lib/esm/modules/Api/Info.js +0 -26
  89. package/lib/esm/modules/Api/index.js +0 -2
  90. package/lib/esm/modules/AudioStation/Song.js +0 -18
  91. package/lib/esm/modules/AudioStation/index.js +0 -7
  92. package/lib/esm/modules/FileStation/Info.js +0 -20
  93. package/lib/esm/modules/FileStation/List.js +0 -32
  94. package/lib/esm/types/index.js +0 -2
  95. package/lib/types/modules/Api/Info.d.ts +0 -3
  96. package/lib/types/modules/Api/index.d.ts +0 -2
  97. package/lib/types/modules/AudioStation/Song.d.ts +0 -22
  98. package/lib/types/modules/AudioStation/index.d.ts +0 -7
  99. package/lib/types/modules/index.d.ts +0 -16
  100. package/lib/types/types/index.d.ts +0 -2
  101. /package/lib/{types/cli → cli}/apis.d.ts +0 -0
  102. /package/lib/{types/cli → cli}/config.d.ts +0 -0
  103. /package/lib/{types/cli/index.d.ts → cli/core.d.ts} +0 -0
  104. /package/lib/{types/constants.d.ts → constants.d.ts} +0 -0
  105. /package/lib/{esm/constants.js → constants.js} +0 -0
  106. /package/lib/{esm/modules/FileStation/File.js → decorators/bindMethods.d.ts} +0 -0
  107. /package/lib/{types/helpers.d.ts → helpers.d.ts} +0 -0
  108. /package/lib/{types/modules/FileStation/File.d.ts → modules/AudioStation/Cover.d.ts} +0 -0
  109. /package/lib/types/{types/request.d.ts → request.d.ts} +0 -0
  110. /package/lib/{esm/types → types}/request.js +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Chris Song
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,26 +1,97 @@
1
1
  # Synology Api
2
2
 
3
- nodejs synology api wrapper
3
+ ⚠️ The project is under development ...
4
4
 
5
- ## Usage
5
+ [中文文档](./README-zh_CN.md)
6
+ Synology Api Node.js wrapper, can be used in Browser、CLI or Nodejs to interact with Synology NAS.
7
+ You can use domain or ip address, also supports Synology Quick Connect connect Synology server.
8
+ All apis from [https://kb.synology.cn](https://kb.synology.cn/zh-cn/search?query=API&services%5B%5D=File_Station)
9
+
10
+ ## Install
11
+
12
+ ```
13
+ npm install @fett/synology-api
14
+ ```
15
+
16
+ ## Use In Browser
17
+
18
+ First you need to confirm that you can access across domains,for example in the React Native environment
19
+
20
+ ```js
21
+ import { SynologyApi } from "@fett/synology-api/browser";
22
+ // Create a new instance
23
+ const api = new SynologyApi({
24
+ server: "https://192.168.1.1:5001",
25
+ username: "username",
26
+ password: "password",
27
+ });
28
+ // you can now use the api by calling the methods
29
+ const info = await api.fs.getInfo();
30
+ console.log(info);
31
+ ```
32
+
33
+ ## Use In Node.js
6
34
 
7
35
  ```js
8
36
  import SynologyApi from '@fett/synology-api';
9
37
 
10
- // Create a new instance
11
38
  const synologyApi = new SynologyApi(
12
- server: "https:192.168.1.1:5001",
13
- username: "admin",
14
- password: "password",
39
+ server: "https://192.168.1.1:5001",
40
+ username: "username",
41
+ password: "password",
15
42
  );
16
43
 
17
- // you can now use the api by calling the methods
18
44
  const info = await synologyApi.FileStation.getInfo();
45
+ console.log(info);
46
+ ```
47
+
48
+ ## Use In CLI
19
49
 
50
+ First install the package globally
51
+
52
+ ```bash
53
+ npm i -g @fett/synology-api
54
+ ```
55
+
56
+ Then run cmd help message
57
+
58
+ ```bash
59
+ syno --help
60
+ ```
61
+
62
+ run `syno config -h` you will see the help message
63
+
64
+ ```bash
65
+ Usage: synology config [options] [command]
66
+
67
+ synology api config management
68
+
69
+ Options:
70
+ -h, --help display help for command
71
+
72
+ Commands:
73
+ ls List all the connection config
74
+ add [options] [name] Add connection config
75
+ use [name] Change current connection
76
+ del [name] Remove a connection
77
+ rename <name> <newName> Change connection name
78
+ help [command] display help for command
79
+ ```
80
+
81
+ add a connect configuration
82
+
83
+ ```bash
84
+ syno config add newConnetionName --server=https://192.168.1.1:5001 --username=admin --password=password
20
85
  ```
21
86
 
22
- ## API
87
+ then you can use it and exec command
88
+
89
+ ```bash
90
+ syno config use newConnetionName
23
91
 
24
- ### FileStation
92
+ syno fs getInfo --pretty # print file system info
93
+
94
+ ```
25
95
 
26
- ### AudioStation
96
+ ## License
97
+ [MIT](https://github.com/ChrisSong1994/synology-api/blob/main/LICENSE)
@@ -0,0 +1,47 @@
1
+ import { program } from "commander";
2
+ import { loadConfig } from "./config.js";
3
+ import { SynologyApi } from "../core.js";
4
+ import { SynologyApiKeys } from "../modules/index.js";
5
+ export const onMethodsCall = (module) => async (method, options) => {
6
+ const config = await loadConfig();
7
+ const params = JSON.parse(options.params || "{}");
8
+ const pretty = options.pretty;
9
+ const synologyApi = new SynologyApi(config.connections[config.used]);
10
+ if (synologyApi[module]?.[method]) {
11
+ const result = await synologyApi[module]?.[method](params);
12
+ if (pretty) {
13
+ console.log(JSON.stringify(result, null, 2));
14
+ }
15
+ else {
16
+ console.log(JSON.stringify(result));
17
+ }
18
+ }
19
+ synologyApi.disconnect();
20
+ process.exit(0);
21
+ };
22
+ // register all modules
23
+ const apisCmdRegisterInfo = [
24
+ {
25
+ cmd: SynologyApiKeys.FileStation,
26
+ alias: SynologyApiKeys.fs,
27
+ },
28
+ {
29
+ cmd: SynologyApiKeys.AudioStation,
30
+ alias: SynologyApiKeys.as,
31
+ },
32
+ {
33
+ cmd: SynologyApiKeys.VideoStation,
34
+ alias: SynologyApiKeys.vs,
35
+ },
36
+ ];
37
+ export const apiCmdRegister = () => {
38
+ apisCmdRegisterInfo.forEach((info) => {
39
+ program
40
+ .command(`${info.cmd} [methods]`)
41
+ .alias(info.alias)
42
+ .option("-p,--params <params>", `${info.cmd} methods params`)
43
+ .option("--pretty", "Prettyprint JSON Output")
44
+ .description(`Synology ${info.cmd} method call`)
45
+ .action(onMethodsCall(info.cmd));
46
+ });
47
+ };
@@ -1,47 +1,38 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import fse from "fs-extra";
11
2
  import path from "path";
12
3
  import os from "os";
13
4
  import { program } from "commander";
14
5
  import chalk from "chalk";
15
- import { isLowerCaseEqual, printMessages, geneDashLine } from "./helper";
6
+ import { isLowerCaseEqual, printMessages, geneDashLine } from "./helper.js";
16
7
  const CONFIG_FILE_PATH = process.env.NODE_ENV === "development"
17
8
  ? path.join(process.cwd(), "./.userdata/.synology-api.json") // development
18
9
  : path.join(os.homedir(), "./.synology-api.json");
19
10
  // load config
20
- export const loadConfig = () => __awaiter(void 0, void 0, void 0, function* () {
21
- if (!(yield fse.pathExists(CONFIG_FILE_PATH))) {
22
- yield fse.ensureFile(CONFIG_FILE_PATH);
23
- yield fse.writeJSON(CONFIG_FILE_PATH, {
11
+ export const loadConfig = async () => {
12
+ if (!(await fse.pathExists(CONFIG_FILE_PATH))) {
13
+ await fse.ensureFile(CONFIG_FILE_PATH);
14
+ await fse.writeJSON(CONFIG_FILE_PATH, {
24
15
  used: "",
25
16
  connections: {},
26
17
  });
27
18
  }
28
- const config = yield fse.readJSON(CONFIG_FILE_PATH);
19
+ const config = await fse.readJSON(CONFIG_FILE_PATH);
29
20
  return config;
30
- });
21
+ };
31
22
  // update config
32
- export const updateConfig = (config) => __awaiter(void 0, void 0, void 0, function* () {
33
- yield fse.writeJSON(CONFIG_FILE_PATH, config);
34
- });
23
+ export const updateConfig = async (config) => {
24
+ await fse.writeJSON(CONFIG_FILE_PATH, config);
25
+ };
35
26
  // check config
36
- export const checkConfig = () => __awaiter(void 0, void 0, void 0, function* () { });
27
+ export const checkConfig = async () => { };
37
28
  export const configCmdRegister = () => {
38
29
  const configCmd = program.command("config").description("synology api config management");
39
30
  // list connection config
40
31
  configCmd
41
32
  .command("ls")
42
33
  .description("List all the connection config")
43
- .action(() => __awaiter(void 0, void 0, void 0, function* () {
44
- const config = yield loadConfig();
34
+ .action(async () => {
35
+ const config = await loadConfig();
45
36
  const keys = Object.keys(config.connections);
46
37
  const dashLineLength = Math.max(...keys.map((key) => key.length)) + 3;
47
38
  const messages = keys.map((key) => {
@@ -57,7 +48,7 @@ export const configCmdRegister = () => {
57
48
  connection.password);
58
49
  });
59
50
  printMessages(messages);
60
- }));
51
+ });
61
52
  // add connection
62
53
  configCmd
63
54
  .command("add [name]")
@@ -65,24 +56,30 @@ export const configCmdRegister = () => {
65
56
  .requiredOption("-s, --server <server>", "Synology server domain or QuickConnect ID ")
66
57
  .requiredOption("-u, --username <username>", "username")
67
58
  .requiredOption("-p, --password <password>", "password")
68
- .action((name, options) => __awaiter(void 0, void 0, void 0, function* () {
59
+ .action(async (name, options) => {
69
60
  if (!name.trim())
70
61
  throw new Error("Plaease input connection name");
71
62
  // 实际代码中应保存配置到文件
72
- const config = yield loadConfig();
73
- const newConfig = Object.assign(Object.assign({}, config), { connections: Object.assign(Object.assign({}, config.connections), { [name]: {
63
+ const config = await loadConfig();
64
+ const newConfig = {
65
+ ...config,
66
+ connections: {
67
+ ...config.connections,
68
+ [name]: {
74
69
  server: options.server,
75
70
  username: options.username,
76
71
  password: options.password,
77
- } }) });
78
- yield updateConfig(newConfig);
79
- }));
72
+ },
73
+ },
74
+ };
75
+ await updateConfig(newConfig);
76
+ });
80
77
  // use connection
81
78
  configCmd
82
79
  .command("use [name]")
83
80
  .description("Change current connection")
84
- .action((name) => __awaiter(void 0, void 0, void 0, function* () {
85
- const config = yield loadConfig();
81
+ .action(async (name) => {
82
+ const config = await loadConfig();
86
83
  if (config.used === name)
87
84
  return;
88
85
  if (!config.connections[name]) {
@@ -90,15 +87,15 @@ export const configCmdRegister = () => {
90
87
  return;
91
88
  }
92
89
  config.used = name;
93
- yield updateConfig(config);
94
- }));
90
+ await updateConfig(config);
91
+ });
95
92
  // del connection
96
93
  configCmd
97
94
  .command("del [name]")
98
95
  .description("Remove a connection")
99
- .action((name) => __awaiter(void 0, void 0, void 0, function* () {
96
+ .action(async (name) => {
100
97
  console.log("Remove a connection", name);
101
- const config = yield loadConfig();
98
+ const config = await loadConfig();
102
99
  // 删除配置
103
100
  if (!config.connections[name]) {
104
101
  console.log("Connection not found");
@@ -108,14 +105,14 @@ export const configCmdRegister = () => {
108
105
  if (config.used === name) {
109
106
  config.used = "";
110
107
  }
111
- yield updateConfig(config);
112
- }));
108
+ await updateConfig(config);
109
+ });
113
110
  // Change connection name
114
111
  configCmd
115
112
  .command("rename <name> <newName>")
116
113
  .description("Change connection name")
117
- .action((name, newName) => __awaiter(void 0, void 0, void 0, function* () {
118
- const config = yield loadConfig();
114
+ .action(async (name, newName) => {
115
+ const config = await loadConfig();
119
116
  if (!config.connections[name]) {
120
117
  console.log("Connection not found");
121
118
  return;
@@ -125,6 +122,6 @@ export const configCmdRegister = () => {
125
122
  if (config.used === name) {
126
123
  config.used = newName;
127
124
  }
128
- yield updateConfig(config);
129
- }));
125
+ await updateConfig(config);
126
+ });
130
127
  };
@@ -0,0 +1,20 @@
1
+ import fse from "fs-extra";
2
+ import path from "path";
3
+ import { program } from "commander";
4
+ import { configCmdRegister } from "./config.js";
5
+ import { apiCmdRegister } from "./apis.js";
6
+ import { __dirname } from "./helper.js";
7
+ export async function loadCli() {
8
+ // git program info
9
+ const pkg = await fse.readJSON(path.join(__dirname(), "../../package.json"));
10
+ // command
11
+ program
12
+ .name("synology")
13
+ .usage("<command> [options]")
14
+ .description("synology api cli tool")
15
+ .version(pkg.version, "-v, --version", "output the version number");
16
+ // register commands
17
+ configCmdRegister();
18
+ apiCmdRegister();
19
+ program.parse(process.argv);
20
+ }
@@ -1,3 +1,4 @@
1
+ export declare const __dirname: () => string;
1
2
  export declare function printMessages(messages: string[]): void;
2
3
  export declare function isLowerCaseEqual(str1: string, str2: string): boolean;
3
4
  export declare function padding(message?: string, before?: number, after?: number): string;
@@ -1,4 +1,10 @@
1
1
  import chalk from "chalk";
2
+ import { fileURLToPath } from "url";
3
+ import { dirname } from "path";
4
+ export const __dirname = () => {
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ return dirname(__filename);
7
+ };
2
8
  export function printMessages(messages) {
3
9
  console.log(messages.join("\n"));
4
10
  }
@@ -8,12 +14,10 @@ export function isLowerCaseEqual(str1, str2) {
8
14
  }
9
15
  return !str1 && !str2;
10
16
  }
11
- export function padding(message = '', before = 1, after = 1) {
12
- return (new Array(before).fill(' ').join('') +
13
- message +
14
- new Array(after).fill(' ').join(''));
17
+ export function padding(message = "", before = 1, after = 1) {
18
+ return new Array(before).fill(" ").join("") + message + new Array(after).fill(" ").join("");
15
19
  }
16
20
  export function geneDashLine(message, length) {
17
- const finalMessage = new Array(Math.max(2, length - message.length + 2)).join('-');
21
+ const finalMessage = new Array(Math.max(2, length - message.length + 2)).join("-");
18
22
  return padding(chalk.dim(finalMessage));
19
23
  }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { loadCli } from "./core.js";
3
+ (async () => {
4
+ await loadCli();
5
+ })();
@@ -1,4 +1,4 @@
1
- import { BaseSynologyApi } from "./modules";
1
+ import { BaseSynologyApi } from "./modules/index.js";
2
2
  export interface SynologyApiOptions {
3
3
  server: string;
4
4
  username: string;
package/lib/core.js ADDED
@@ -0,0 +1,99 @@
1
+ // reference: https://kb.synology.com/zh-tw/DSM/tutorial/What_websites_does_Synology_NAS_connect_to_when_running_services_or_updating_software
2
+ import ky from "ky";
3
+ import { BaseSynologyApi } from "./modules/index.js";
4
+ import { isHttpUrl, getApiKey, isUndfined } from "./utils.js";
5
+ import { getServerInfo } from "./helpers.js";
6
+ import { login, logout, getApiInfo } from "./modules/Api/index.js";
7
+ import { resWithErrorCode } from "./errorcodes.js";
8
+ export class SynologyApi extends BaseSynologyApi {
9
+ constructor(options) {
10
+ super();
11
+ this.isConnecting = false;
12
+ this.authInfo = null;
13
+ this.apiInfo = {};
14
+ this.server = options.server;
15
+ this.username = options.username;
16
+ this.password = options.password;
17
+ this.baseUrl = `${this.server}/webapi/`;
18
+ }
19
+ async connect() {
20
+ // if quickconnect id
21
+ if (!isHttpUrl(this.server)) {
22
+ this.server = await getServerInfo(this.server);
23
+ // reset base url
24
+ this.baseUrl = `${this.server}/webapi/`;
25
+ }
26
+ try {
27
+ const result = await login(this);
28
+ this.authInfo = result.data;
29
+ this.isConnecting = true;
30
+ await this._getApiInfo();
31
+ return true;
32
+ }
33
+ catch (err) {
34
+ console.error(err);
35
+ return false;
36
+ }
37
+ }
38
+ async disconnect() {
39
+ try {
40
+ await logout(this);
41
+ this.authInfo = null;
42
+ this.apiInfo = {};
43
+ this.isConnecting = false;
44
+ return true;
45
+ }
46
+ catch (err) {
47
+ console.error(err);
48
+ return false;
49
+ }
50
+ }
51
+ async _getApiInfo() {
52
+ try {
53
+ const result = await getApiInfo(this);
54
+ this.apiInfo = result.data;
55
+ }
56
+ catch (err) {
57
+ console.error(err);
58
+ }
59
+ }
60
+ hasApi(apiName) {
61
+ if (!this.isConnecting) {
62
+ throw new Error("Not connected");
63
+ }
64
+ return Object.prototype.hasOwnProperty.call(this.apiInfo, apiName);
65
+ }
66
+ async run(apiName, options) {
67
+ if (!this.isConnecting) {
68
+ const res = await this.connect();
69
+ if (!res) {
70
+ throw new Error("Not connected");
71
+ }
72
+ }
73
+ if (!this.hasApi(apiName)) {
74
+ throw new Error(`${apiName} not found`);
75
+ }
76
+ const { method = "get", params, data, headers } = options;
77
+ const api = this.apiInfo[apiName];
78
+ const url = `${this.baseUrl}${api.path}`;
79
+ const externalParams = {
80
+ api: apiName,
81
+ version: api.maxVersion,
82
+ _sid: this.authInfo.sid,
83
+ ...params,
84
+ };
85
+ let result = null;
86
+ if (method === "get") {
87
+ result = await ky.get(url, { searchParams: externalParams, headers }).json();
88
+ }
89
+ if (method === "post") {
90
+ result = await ky.post(url, { searchParams: externalParams, json: data, headers }).json();
91
+ }
92
+ // match error code msg
93
+ const apiKey = getApiKey(apiName);
94
+ if (!isUndfined(apiKey)) {
95
+ result = resWithErrorCode(apiKey, result);
96
+ }
97
+ return result;
98
+ }
99
+ }
@@ -0,0 +1,45 @@
1
+ // export type Method = (...args: any[]) => Promise<any>;
2
+ // type MethodKeys<T> = {
3
+ // [K in keyof T]: {
4
+ // [M in keyof T[K]]: Method;
5
+ // };
6
+ // }[keyof T];
7
+ // /**
8
+ // * 将源对象的方法绑定到目标类的原型上,并确保方法的 this 指向类实例
9
+ // * @param source 包含要绑定方法的对象
10
+ // */
11
+ // export function BindMethods<T extends object>(source: T): ClassDecorator {
12
+ // return function (target: { prototype: any }) {
13
+ // // 获取源对象的所有方法键
14
+ // const methodKeys = Object.getOwnPropertyNames(source).filter(
15
+ // (key): key is MethodKeys<T> => typeof source[key] === "function"
16
+ // );
17
+ // // 为每个方法创建绑定版本并定义到原型上
18
+ // methodKeys.forEach((methodName) => {
19
+ // const originalMethod = source[methodName] as Function;
20
+ // // 创建绑定方法的描述符
21
+ // const descriptor: PropertyDescriptor = {
22
+ // configurable: true,
23
+ // enumerable: false, // 使方法不可枚举
24
+ // // 使用 getter 确保每个实例都有自己的绑定函数
25
+ // get() {
26
+ // // 跳过原型访问,直接返回原始方法
27
+ // if (this === target.prototype || this === undefined) {
28
+ // return originalMethod;
29
+ // }
30
+ // // 为实例创建绑定方法并缓存
31
+ // const boundMethod = originalMethod.bind(this);
32
+ // Object.defineProperty(this, methodName, {
33
+ // value: boundMethod,
34
+ // configurable: true,
35
+ // writable: true,
36
+ // enumerable: false,
37
+ // });
38
+ // return boundMethod;
39
+ // },
40
+ // };
41
+ // // 将绑定方法定义到类原型上
42
+ // Object.defineProperty(target.prototype, methodName, descriptor);
43
+ // });
44
+ // };
45
+ // }
@@ -1,5 +1,5 @@
1
- import { SynologyApiKeys } from "./modules";
2
- import { SynologyApiResponse } from "./types";
1
+ import { SynologyApiKeys } from "./modules/index.js";
2
+ import { SynologyApiResponse } from "./types/index.js";
3
3
  export declare const SYNOLOGY_ERROR_CODES: {
4
4
  [SynologyApiKeys.FileStation]: {
5
5
  400: string;
@@ -1,5 +1,5 @@
1
- import { SynologyApiKeys } from "./modules";
2
- import { isUndfined } from "./utils";
1
+ import { SynologyApiKeys } from "./modules/index.js";
2
+ import { isUndfined } from "./utils.js";
3
3
  const CODE_SUCCESS = 0;
4
4
  const CODE_UNKNOWN = 9999;
5
5
  export const SYNOLOGY_ERROR_CODES = {
@@ -88,10 +88,15 @@ export const SYNOLOGY_ERROR_CODES = {
88
88
  },
89
89
  };
90
90
  export const resWithErrorCode = (apiKey, res) => {
91
- var _a, _b;
92
91
  const errorCodes = SYNOLOGY_ERROR_CODES[apiKey];
93
- const code = (_a = res === null || res === void 0 ? void 0 : res.error) === null || _a === void 0 ? void 0 : _a.code;
92
+ const code = res?.error?.code;
94
93
  if (isUndfined(code))
95
94
  return res;
96
- return Object.assign(Object.assign({}, res), { error: Object.assign(Object.assign({}, res.error), { message: ((_b = res === null || res === void 0 ? void 0 : res.error) === null || _b === void 0 ? void 0 : _b.message) || (errorCodes === null || errorCodes === void 0 ? void 0 : errorCodes[code]) || "Unknown error" }) });
95
+ return {
96
+ ...res,
97
+ error: {
98
+ ...res.error,
99
+ message: res?.error?.message || errorCodes?.[code] || "Unknown error",
100
+ },
101
+ };
97
102
  };
package/lib/helpers.js ADDED
@@ -0,0 +1,70 @@
1
+ import ky from "ky";
2
+ import { GLOBAL_QUICK_CONNECT_URL, QUICK_CONNECT_PINGPANG_API } from "./constants.js";
3
+ const getServersFromServerInfo = async (serverInfo) => {
4
+ // WAN IP
5
+ if (serverInfo?.server?.external?.ip) {
6
+ const server = `http://${serverInfo.server.external.ip}:${serverInfo.service.port}`;
7
+ if (await pingpang(server)) {
8
+ return server;
9
+ }
10
+ }
11
+ // proxy server
12
+ if (serverInfo?.service?.relay_ip) {
13
+ const server = `http://${serverInfo.service.relay_ip}:${serverInfo.service.relay_port}`;
14
+ const res = await pingpang(server);
15
+ if (res) {
16
+ return server;
17
+ }
18
+ }
19
+ // lan ip
20
+ if (serverInfo?.server?.interface?.[0]) {
21
+ const server = `http://${serverInfo.server.interface?.[0].ip}:${serverInfo.service.port}`;
22
+ if (await pingpang(server)) {
23
+ return server;
24
+ }
25
+ }
26
+ };
27
+ export const getServerInfo = async (quickConnectId) => {
28
+ const params = {
29
+ version: 1,
30
+ id: "dsm",
31
+ serverID: quickConnectId,
32
+ get_ca_fingerprints: true,
33
+ command: "get_server_info",
34
+ };
35
+ const serverInfo = await ky.post(GLOBAL_QUICK_CONNECT_URL, { json: params }).json();
36
+ if (!serverInfo?.service?.relay_ip && !serverInfo?.service?.relay_port) {
37
+ const relayRequestParams = {
38
+ version: 1,
39
+ id: "dsm",
40
+ serverID: quickConnectId,
41
+ platform: "web",
42
+ command: "request_tunnel",
43
+ };
44
+ const result = await ky
45
+ .post(`https://${serverInfo.env.control_host}/Serv.php`, { json: relayRequestParams })
46
+ .json();
47
+ return getServersFromServerInfo(result);
48
+ }
49
+ else {
50
+ return getServersFromServerInfo(serverInfo);
51
+ }
52
+ };
53
+ // pingpang
54
+ export const pingpang = async (server) => {
55
+ try {
56
+ const result = await ky
57
+ .get(`${server}/${QUICK_CONNECT_PINGPANG_API}`)
58
+ .json();
59
+ if (result.success) {
60
+ return true;
61
+ }
62
+ else {
63
+ return false;
64
+ }
65
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
+ }
67
+ catch (_err) {
68
+ return false;
69
+ }
70
+ };
package/lib/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { SynologyApi } from "./core.js";
2
+ export * from "./types/index.js";
3
+ export * from "./core.js";
4
+ export default SynologyApi;
package/lib/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { SynologyApi } from "./core.js";
2
+ export * from "./types/index.js";
3
+ export * from "./core.js";
4
+ export default SynologyApi;