@fett/synology-api 0.0.1-beta.9 → 0.0.2-beta.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.
- package/LICENSE +21 -0
- package/README.md +72 -11
- package/lib/cli/apis.d.ts +7 -0
- package/lib/cli/apis.js +57 -0
- package/lib/{types/cli → cli}/config.d.ts +0 -1
- package/lib/{esm/cli → cli}/config.js +40 -45
- package/lib/cli/core.js +20 -0
- package/lib/{types/cli → cli}/helper.d.ts +1 -0
- package/lib/{esm/cli → cli}/helper.js +9 -5
- package/lib/cli/index.d.ts +2 -0
- package/lib/cli/index.js +5 -0
- package/lib/{types/core.d.ts → core.d.ts} +1 -1
- package/lib/core.js +99 -0
- package/lib/decorators/bindMethods.d.ts +5 -0
- package/lib/decorators/bindMethods.js +29 -0
- package/lib/decorators/index.d.ts +1 -0
- package/lib/decorators/index.js +1 -0
- package/lib/{types/errorcodes.d.ts → errorcodes.d.ts} +2 -2
- package/lib/{esm/errorcodes.js → errorcodes.js} +10 -5
- package/lib/helpers.js +73 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +4 -0
- package/lib/{types/modules → modules}/Api/Auth.d.ts +2 -2
- package/lib/modules/Api/Auth.js +30 -0
- package/lib/modules/Api/Info.d.ts +3 -0
- package/lib/modules/Api/Info.js +15 -0
- package/lib/modules/Api/index.d.ts +2 -0
- package/lib/modules/Api/index.js +2 -0
- package/lib/modules/AudioStation/Album.d.ts +30 -0
- package/lib/modules/AudioStation/Album.js +17 -0
- package/lib/modules/AudioStation/Artist.d.ts +25 -0
- package/lib/modules/AudioStation/Artist.js +17 -0
- package/lib/modules/AudioStation/Folder.d.ts +24 -0
- package/lib/modules/AudioStation/Folder.js +17 -0
- package/lib/modules/AudioStation/Info.d.ts +45 -0
- package/lib/modules/AudioStation/Info.js +9 -0
- package/lib/modules/AudioStation/Lyrics.d.ts +25 -0
- package/lib/modules/AudioStation/Lyrics.js +23 -0
- package/lib/modules/AudioStation/Playlist.d.ts +34 -0
- package/lib/modules/AudioStation/Playlist.js +38 -0
- package/lib/modules/AudioStation/Search.d.ts +53 -0
- package/lib/modules/AudioStation/Search.js +17 -0
- package/lib/modules/AudioStation/Song.d.ts +33 -0
- package/lib/modules/AudioStation/Song.js +27 -0
- package/lib/modules/AudioStation/index.d.ts +23 -0
- package/lib/modules/AudioStation/index.js +23 -0
- package/lib/modules/Auth/AuthKey.d.ts +8 -0
- package/lib/modules/Auth/AuthKey.js +12 -0
- package/lib/modules/Auth/index.d.ts +7 -0
- package/lib/modules/Auth/index.js +8 -0
- package/lib/modules/FileStation/File.d.ts +0 -0
- package/lib/modules/FileStation/File.js +0 -0
- package/lib/{types/modules → modules}/FileStation/Info.d.ts +1 -1
- package/lib/modules/FileStation/Info.js +9 -0
- package/lib/{types/modules → modules}/FileStation/List.d.ts +1 -1
- package/lib/modules/FileStation/List.js +30 -0
- package/lib/{types/modules → modules}/FileStation/index.d.ts +4 -4
- package/lib/{esm/modules → modules}/FileStation/index.js +4 -4
- package/lib/modules/VideoStation/File.d.ts +48 -0
- package/lib/modules/VideoStation/File.js +20 -0
- package/lib/modules/VideoStation/HomeVideo.d.ts +6 -0
- package/lib/modules/VideoStation/HomeVideo.js +10 -0
- package/lib/modules/VideoStation/Info.d.ts +44 -0
- package/lib/modules/VideoStation/Info.js +9 -0
- package/lib/modules/VideoStation/Library.d.ts +70 -0
- package/lib/modules/VideoStation/Library.js +23 -0
- package/lib/modules/VideoStation/Movie.d.ts +27 -0
- package/lib/modules/VideoStation/Movie.js +13 -0
- package/lib/modules/VideoStation/Streaming.d.ts +11 -0
- package/lib/modules/VideoStation/Streaming.js +24 -0
- package/lib/modules/VideoStation/index.d.ts +15 -0
- package/lib/modules/VideoStation/index.js +16 -0
- package/lib/modules/VideoStation/types.d.ts +27 -0
- package/lib/modules/VideoStation/types.js +1 -0
- package/lib/modules/index.d.ts +27 -0
- package/lib/modules/index.js +75 -0
- package/lib/types/{types/apiInfo.d.ts → apiInfo.d.ts} +15 -2
- package/lib/{esm/types → types}/apiInfo.js +15 -1
- package/lib/types/index.d.ts +2 -5
- package/lib/types/index.js +2 -0
- package/lib/{types/utils.d.ts → utils.d.ts} +1 -0
- package/lib/{esm/utils.js → utils.js} +3 -0
- package/package.json +27 -24
- package/bin/syno +0 -6
- package/lib/cjs/index.js +0 -728
- package/lib/esm/cli/apis.js +0 -53
- package/lib/esm/cli/index.js +0 -30
- package/lib/esm/core.js +0 -111
- package/lib/esm/helpers.js +0 -81
- package/lib/esm/index.js +0 -5
- package/lib/esm/modules/Api/Auth.js +0 -43
- package/lib/esm/modules/Api/Info.js +0 -26
- package/lib/esm/modules/Api/index.js +0 -2
- package/lib/esm/modules/AudioStation/Song.js +0 -18
- package/lib/esm/modules/AudioStation/index.js +0 -7
- package/lib/esm/modules/FileStation/Info.js +0 -20
- package/lib/esm/modules/FileStation/List.js +0 -32
- package/lib/esm/modules/index.js +0 -46
- package/lib/esm/types/index.js +0 -2
- package/lib/types/cli/apis.d.ts +0 -6
- package/lib/types/modules/Api/Info.d.ts +0 -3
- package/lib/types/modules/Api/index.d.ts +0 -2
- package/lib/types/modules/AudioStation/Song.d.ts +0 -22
- package/lib/types/modules/AudioStation/index.d.ts +0 -7
- package/lib/types/modules/index.d.ts +0 -16
- package/lib/types/types/index.d.ts +0 -2
- /package/lib/{types/cli/index.d.ts → cli/core.d.ts} +0 -0
- /package/lib/{types/constants.d.ts → constants.d.ts} +0 -0
- /package/lib/{esm/constants.js → constants.js} +0 -0
- /package/lib/{types/helpers.d.ts → helpers.d.ts} +0 -0
- /package/lib/{esm/modules/FileStation/File.js → modules/AudioStation/Cover.d.ts} +0 -0
- /package/lib/{types/modules/FileStation/File.d.ts → modules/AudioStation/Cover.js} +0 -0
- /package/lib/types/{types/request.d.ts → request.d.ts} +0 -0
- /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,87 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a >
|
|
3
|
+
<img width="100" src="assets/logo.png">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
<h1 align="center">Javascript Synology Api</h1>
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
⚠️ The project is under development ...
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
Synology Api Javascript wrapper, can be used in Browser、CLI or Nodejs to interact with Synology NAS.
|
|
11
|
+
You can use domain or ip address, also supports Synology Quick Connect connect Synology server.
|
|
12
|
+
All apis from [https://kb.synology.cn](https://kb.synology.cn/zh-cn/search?query=API&services%5B%5D=File_Station)
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
npm install @fett/synology-api
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Use In Browser or Node.js
|
|
21
|
+
|
|
22
|
+
First you need to confirm that you can access across domains,for example in the React Native environment
|
|
6
23
|
|
|
7
24
|
```js
|
|
8
25
|
import SynologyApi from '@fett/synology-api';
|
|
9
26
|
|
|
10
|
-
// Create a new instance
|
|
11
27
|
const synologyApi = new SynologyApi(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
server: "https://192.168.1.1:5001",
|
|
29
|
+
username: "username",
|
|
30
|
+
password: "password",
|
|
15
31
|
);
|
|
16
32
|
|
|
17
|
-
// you can now use the api by calling the methods
|
|
18
33
|
const info = await synologyApi.FileStation.getInfo();
|
|
34
|
+
console.log(info);
|
|
35
|
+
```
|
|
19
36
|
|
|
37
|
+
## Use In CLI
|
|
38
|
+
|
|
39
|
+
First install the package globally
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm i -g @fett/synology-api
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then run cmd help message
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
syno --help
|
|
20
49
|
```
|
|
21
50
|
|
|
22
|
-
|
|
51
|
+
run `syno config -h` you will see the help message
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
Usage: synology config [options] [command]
|
|
55
|
+
|
|
56
|
+
synology api config management
|
|
57
|
+
|
|
58
|
+
Options:
|
|
59
|
+
-h, --help display help for command
|
|
60
|
+
|
|
61
|
+
Commands:
|
|
62
|
+
ls List all the connection config
|
|
63
|
+
add [options] [name] Add connection config
|
|
64
|
+
use [name] Change current connection
|
|
65
|
+
del [name] Remove a connection
|
|
66
|
+
rename <name> <newName> Change connection name
|
|
67
|
+
help [command] display help for command
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
add a connect configuration
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
syno config add newConnetionName --server=https://192.168.1.1:5001 --username=admin --password=password
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
then you can use it and exec command
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
syno config use newConnetionName
|
|
80
|
+
|
|
81
|
+
syno fs getInfo --pretty # print file system info
|
|
82
|
+
|
|
83
|
+
```
|
|
23
84
|
|
|
24
|
-
|
|
85
|
+
## License
|
|
25
86
|
|
|
26
|
-
|
|
87
|
+
[MIT](https://github.com/ChrisSong1994/synology-api/blob/main/LICENSE)
|
package/lib/cli/apis.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { program } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { loadConfig } from "./config.js";
|
|
4
|
+
import { SynologyApi } from "../core.js";
|
|
5
|
+
import { SynologyApiModules, SynologyApiMethods } from "../modules/index.js";
|
|
6
|
+
import { printMessages } from "./helper.js";
|
|
7
|
+
export const onMethodCall = (module) => async (method, options) => {
|
|
8
|
+
const config = await loadConfig();
|
|
9
|
+
const params = JSON.parse(options.params || "{}");
|
|
10
|
+
const pretty = options.pretty;
|
|
11
|
+
const connection = config.connections?.[config.used];
|
|
12
|
+
if (!connection) {
|
|
13
|
+
throw new Error("Connection undefined");
|
|
14
|
+
}
|
|
15
|
+
if (!SynologyApiMethods[module]) {
|
|
16
|
+
throw new Error(`Module ${module} not found`);
|
|
17
|
+
}
|
|
18
|
+
// list all module methods
|
|
19
|
+
if (!method && options.list) {
|
|
20
|
+
const methods = SynologyApiMethods[module];
|
|
21
|
+
console.log(chalk.greenBright(`${module} methods list:`));
|
|
22
|
+
return printMessages(Object.keys(methods));
|
|
23
|
+
}
|
|
24
|
+
if (!SynologyApiMethods[module]?.[method]) {
|
|
25
|
+
throw new Error(`Module method ${module} not found`);
|
|
26
|
+
}
|
|
27
|
+
if (typeof SynologyApiMethods[module][method] !== "function") {
|
|
28
|
+
throw new Error(`Module method ${module} is not a function`);
|
|
29
|
+
}
|
|
30
|
+
const synologyApi = new SynologyApi(connection);
|
|
31
|
+
const result = await synologyApi[module]?.[method](params);
|
|
32
|
+
if (pretty) {
|
|
33
|
+
console.log(JSON.stringify(result, null, 2));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.log(JSON.stringify(result));
|
|
37
|
+
}
|
|
38
|
+
synologyApi.disconnect();
|
|
39
|
+
process.exit(0);
|
|
40
|
+
};
|
|
41
|
+
// register all modules
|
|
42
|
+
const apisCmdRegisterInfo = SynologyApiModules.map((module) => ({
|
|
43
|
+
cmd: module.KEY,
|
|
44
|
+
alias: module.ALIAS_KEY,
|
|
45
|
+
}));
|
|
46
|
+
export const apiCmdRegister = () => {
|
|
47
|
+
apisCmdRegisterInfo.forEach((info) => {
|
|
48
|
+
program
|
|
49
|
+
.command(`${info.cmd} [method]`)
|
|
50
|
+
.alias(info.alias)
|
|
51
|
+
.option("-p,--params <params>", `${info.cmd} method params`)
|
|
52
|
+
.option("--pretty", "Prettyprint JSON Output")
|
|
53
|
+
.option("-l,--list", `list all ${info.cmd} methods`)
|
|
54
|
+
.description(`Synology ${info.cmd} method call`)
|
|
55
|
+
.action(onMethodCall(info.cmd));
|
|
56
|
+
});
|
|
57
|
+
};
|
|
@@ -9,5 +9,4 @@ export type Config = {
|
|
|
9
9
|
};
|
|
10
10
|
export declare const loadConfig: () => Promise<Config>;
|
|
11
11
|
export declare const updateConfig: (config: Config) => Promise<void>;
|
|
12
|
-
export declare const checkConfig: () => Promise<void>;
|
|
13
12
|
export declare const configCmdRegister: () => void;
|
|
@@ -1,47 +1,36 @@
|
|
|
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 = () =>
|
|
21
|
-
if (!(
|
|
22
|
-
|
|
23
|
-
|
|
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 =
|
|
19
|
+
const config = await fse.readJSON(CONFIG_FILE_PATH);
|
|
29
20
|
return config;
|
|
30
|
-
}
|
|
21
|
+
};
|
|
31
22
|
// update config
|
|
32
|
-
export const updateConfig = (config) =>
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
// check config
|
|
36
|
-
export const checkConfig = () => __awaiter(void 0, void 0, void 0, function* () { });
|
|
23
|
+
export const updateConfig = async (config) => {
|
|
24
|
+
await fse.writeJSON(CONFIG_FILE_PATH, config);
|
|
25
|
+
};
|
|
37
26
|
export const configCmdRegister = () => {
|
|
38
27
|
const configCmd = program.command("config").description("synology api config management");
|
|
39
28
|
// list connection config
|
|
40
29
|
configCmd
|
|
41
30
|
.command("ls")
|
|
42
31
|
.description("List all the connection config")
|
|
43
|
-
.action(() =>
|
|
44
|
-
const config =
|
|
32
|
+
.action(async () => {
|
|
33
|
+
const config = await loadConfig();
|
|
45
34
|
const keys = Object.keys(config.connections);
|
|
46
35
|
const dashLineLength = Math.max(...keys.map((key) => key.length)) + 3;
|
|
47
36
|
const messages = keys.map((key) => {
|
|
@@ -57,32 +46,38 @@ export const configCmdRegister = () => {
|
|
|
57
46
|
connection.password);
|
|
58
47
|
});
|
|
59
48
|
printMessages(messages);
|
|
60
|
-
})
|
|
49
|
+
});
|
|
61
50
|
// add connection
|
|
62
51
|
configCmd
|
|
63
|
-
.command("add
|
|
52
|
+
.command("add <name>")
|
|
64
53
|
.description("Add connection config")
|
|
65
54
|
.requiredOption("-s, --server <server>", "Synology server domain or QuickConnect ID ")
|
|
66
55
|
.requiredOption("-u, --username <username>", "username")
|
|
67
56
|
.requiredOption("-p, --password <password>", "password")
|
|
68
|
-
.action((name, options) =>
|
|
57
|
+
.action(async (name, options) => {
|
|
69
58
|
if (!name.trim())
|
|
70
59
|
throw new Error("Plaease input connection name");
|
|
71
60
|
// 实际代码中应保存配置到文件
|
|
72
|
-
const config =
|
|
73
|
-
const newConfig =
|
|
61
|
+
const config = await loadConfig();
|
|
62
|
+
const newConfig = {
|
|
63
|
+
used: config.used || name,
|
|
64
|
+
connections: {
|
|
65
|
+
...config.connections,
|
|
66
|
+
[name]: {
|
|
74
67
|
server: options.server,
|
|
75
68
|
username: options.username,
|
|
76
69
|
password: options.password,
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
await updateConfig(newConfig);
|
|
74
|
+
});
|
|
80
75
|
// use connection
|
|
81
76
|
configCmd
|
|
82
|
-
.command("use
|
|
77
|
+
.command("use <name>")
|
|
83
78
|
.description("Change current connection")
|
|
84
|
-
.action((name) =>
|
|
85
|
-
const config =
|
|
79
|
+
.action(async (name) => {
|
|
80
|
+
const config = await loadConfig();
|
|
86
81
|
if (config.used === name)
|
|
87
82
|
return;
|
|
88
83
|
if (!config.connections[name]) {
|
|
@@ -90,15 +85,15 @@ export const configCmdRegister = () => {
|
|
|
90
85
|
return;
|
|
91
86
|
}
|
|
92
87
|
config.used = name;
|
|
93
|
-
|
|
94
|
-
})
|
|
88
|
+
await updateConfig(config);
|
|
89
|
+
});
|
|
95
90
|
// del connection
|
|
96
91
|
configCmd
|
|
97
|
-
.command("del
|
|
92
|
+
.command("del <name>")
|
|
98
93
|
.description("Remove a connection")
|
|
99
|
-
.action((name) =>
|
|
94
|
+
.action(async (name) => {
|
|
100
95
|
console.log("Remove a connection", name);
|
|
101
|
-
const config =
|
|
96
|
+
const config = await loadConfig();
|
|
102
97
|
// 删除配置
|
|
103
98
|
if (!config.connections[name]) {
|
|
104
99
|
console.log("Connection not found");
|
|
@@ -108,14 +103,14 @@ export const configCmdRegister = () => {
|
|
|
108
103
|
if (config.used === name) {
|
|
109
104
|
config.used = "";
|
|
110
105
|
}
|
|
111
|
-
|
|
112
|
-
})
|
|
106
|
+
await updateConfig(config);
|
|
107
|
+
});
|
|
113
108
|
// Change connection name
|
|
114
109
|
configCmd
|
|
115
110
|
.command("rename <name> <newName>")
|
|
116
111
|
.description("Change connection name")
|
|
117
|
-
.action((name, newName) =>
|
|
118
|
-
const config =
|
|
112
|
+
.action(async (name, newName) => {
|
|
113
|
+
const config = await loadConfig();
|
|
119
114
|
if (!config.connections[name]) {
|
|
120
115
|
console.log("Connection not found");
|
|
121
116
|
return;
|
|
@@ -125,6 +120,6 @@ export const configCmdRegister = () => {
|
|
|
125
120
|
if (config.used === name) {
|
|
126
121
|
config.used = newName;
|
|
127
122
|
}
|
|
128
|
-
|
|
129
|
-
})
|
|
123
|
+
await updateConfig(config);
|
|
124
|
+
});
|
|
130
125
|
};
|
package/lib/cli/core.js
ADDED
|
@@ -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 =
|
|
12
|
-
return
|
|
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
|
}
|
package/lib/cli/index.js
ADDED
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,29 @@
|
|
|
1
|
+
// bind methods to context
|
|
2
|
+
function methodsBundler(cxt, methods) {
|
|
3
|
+
const output = {};
|
|
4
|
+
for (const key in methods) {
|
|
5
|
+
output[key] = methods[key].bind(cxt);
|
|
6
|
+
}
|
|
7
|
+
return output;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* bind methods to BaseSynologyApi prototype
|
|
11
|
+
* @param source
|
|
12
|
+
*/
|
|
13
|
+
export function BindMethods(source) {
|
|
14
|
+
return function (target) {
|
|
15
|
+
const modulesKeys = Object.getOwnPropertyNames(source);
|
|
16
|
+
modulesKeys.forEach((moduleName) => {
|
|
17
|
+
const methods = source[moduleName];
|
|
18
|
+
const descriptor = {
|
|
19
|
+
configurable: false,
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get() {
|
|
22
|
+
return methodsBundler(this, methods);
|
|
23
|
+
},
|
|
24
|
+
set() { },
|
|
25
|
+
};
|
|
26
|
+
Object.defineProperty(target.prototype, moduleName, descriptor);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./bindMethods.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./bindMethods.js";
|
|
@@ -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 =
|
|
92
|
+
const code = res?.error?.code;
|
|
94
93
|
if (isUndfined(code))
|
|
95
94
|
return res;
|
|
96
|
-
return
|
|
95
|
+
return {
|
|
96
|
+
...res,
|
|
97
|
+
error: {
|
|
98
|
+
...res.error,
|
|
99
|
+
message: res?.error?.message || errorCodes?.[code] || "Unknown error",
|
|
100
|
+
},
|
|
101
|
+
};
|
|
97
102
|
};
|