@simplysm/storage 13.0.85 → 13.0.86

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 (2) hide show
  1. package/README.md +154 -91
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,10 +1,36 @@
1
1
  # @simplysm/storage
2
2
 
3
- > Simplysm Package - Storage Module (node)
3
+ FTP, FTPS, SFTP 원격 스토리지 통합 라이브러리. 팩토리 패턴으로 프로토콜에 독립적인 파일 전송을 제공한다.
4
4
 
5
- A unified storage client library for Node.js that provides a consistent API for FTP, FTPS, and SFTP file operations. The factory pattern with automatic connection management makes it easy to perform remote file operations without worrying about connection lifecycle. Internally uses [basic-ftp](https://www.npmjs.com/package/basic-ftp) for FTP/FTPS and [ssh2-sftp-client](https://www.npmjs.com/package/ssh2-sftp-client) for SFTP.
5
+ ## 설치
6
6
 
7
- ## API Reference
7
+ ```bash
8
+ npm install @simplysm/storage
9
+ ```
10
+
11
+ **의존성:** `basic-ftp` (FTP/FTPS), `ssh2-sftp-client` (SFTP), `@simplysm/core-common`
12
+
13
+ ## 빠른 시작
14
+
15
+ ```typescript
16
+ import { StorageFactory } from "@simplysm/storage";
17
+
18
+ // 팩토리 패턴: 연결 → 작업 → 자동 종료
19
+ await StorageFactory.connect("sftp", {
20
+ host: "example.com",
21
+ port: 22,
22
+ user: "deploy",
23
+ password: "secret",
24
+ }, async (storage) => {
25
+ await storage.put("/local/file.zip", "/remote/file.zip");
26
+ await storage.uploadDir("/local/dist", "/remote/www");
27
+ const files = await storage.list("/remote/www");
28
+ const exists = await storage.exists("/remote/file.zip");
29
+ });
30
+ // 콜백 종료 시 연결 자동 해제 (예외 발생해도 보장)
31
+ ```
32
+
33
+ ## 타입 정의
8
34
 
9
35
  ### StorageProtocol
10
36
 
@@ -12,149 +38,186 @@ A unified storage client library for Node.js that provides a consistent API for
12
38
  type StorageProtocol = "ftp" | "ftps" | "sftp";
13
39
  ```
14
40
 
15
- Union type representing the supported storage protocols.
16
-
17
- ---
41
+ | | 설명 | 내부 클라이언트 |
42
+ |-----|------|----------------|
43
+ | `"ftp"` | File Transfer Protocol | `FtpStorageClient(secure=false)` |
44
+ | `"ftps"` | FTP over SSL/TLS | `FtpStorageClient(secure=true)` |
45
+ | `"sftp"` | SSH File Transfer Protocol | `SftpStorageClient` |
18
46
 
19
47
  ### StorageConnConfig
20
48
 
21
49
  ```typescript
22
50
  interface StorageConnConfig {
23
51
  host: string;
24
- port?: number;
52
+ port?: number; // 미지정 시 프로토콜 기본값 사용
25
53
  user?: string;
26
54
  password?: string;
27
55
  }
28
56
  ```
29
57
 
30
- Connection configuration shared across all protocols. When `password` is omitted for SFTP, authentication falls back to SSH agent and `~/.ssh/id_ed25519` key file.
31
-
32
- ---
58
+ - **SFTP에서 `password` 미지정 시**: SSH 인증으로 전환
59
+ 1. `~/.ssh/id_ed25519` 키 파일 자동 탐색
60
+ 2. 키 파싱 실패 시 `SSH_AUTH_SOCK` 환경변수의 SSH 에이전트로 재시도
33
61
 
34
62
  ### FileInfo
35
63
 
36
64
  ```typescript
37
65
  interface FileInfo {
38
- name: string;
39
- isFile: boolean;
66
+ name: string; // 파일/디렉토리 이름
67
+ isFile: boolean; // true: 파일, false: 디렉토리
40
68
  }
41
69
  ```
42
70
 
43
- Represents a file or directory entry returned by `list()`.
71
+ ### Bytes
44
72
 
45
- ---
73
+ `@simplysm/core-common`에서 정의한 바이트 배열 타입 (`Uint8Array` 기반).
46
74
 
47
- ### StorageClient
75
+ ## API 레퍼런스
48
76
 
49
- ```typescript
50
- interface StorageClient {
51
- connect(config: StorageConnConfig): Promise<void>;
52
- mkdir(dirPath: string): Promise<void>;
53
- rename(fromPath: string, toPath: string): Promise<void>;
54
- list(dirPath: string): Promise<FileInfo[]>;
55
- readFile(filePath: string): Promise<Bytes>;
56
- exists(filePath: string): Promise<boolean>;
57
- put(localPathOrBuffer: string | Bytes, storageFilePath: string): Promise<void>;
58
- uploadDir(fromPath: string, toPath: string): Promise<void>;
59
- remove(filePath: string): Promise<void>;
60
- close(): Promise<void>;
61
- }
62
- ```
77
+ ### StorageFactory
63
78
 
64
- Common interface implemented by all storage clients. Using `StorageFactory.connect()` is recommended over calling these methods directly, as it manages the connection lifecycle automatically.
79
+ 프로토콜에 독립적으로 스토리지 클라이언트를 생성하고 연결을 관리하는 팩토리 클래스.
65
80
 
66
- | Method | Description |
67
- |--------|-------------|
68
- | `connect` | Open a connection to the remote server. |
69
- | `mkdir` | Create a directory, including parent directories. |
70
- | `rename` | Rename or move a remote file/directory. |
71
- | `list` | List entries in a remote directory. |
72
- | `readFile` | Read the contents of a remote file as `Bytes`. |
73
- | `exists` | Check whether a remote file or directory exists. |
74
- | `put` | Upload a local file path or byte data to a remote path. |
75
- | `uploadDir` | Upload an entire local directory to a remote path. |
76
- | `remove` | Delete a remote file. |
77
- | `close` | Close the connection. Safe to call when already closed. |
81
+ #### `StorageFactory.connect<R>(type, config, fn): Promise<R>`
78
82
 
79
- ---
83
+ ```typescript
84
+ static async connect<R>(
85
+ type: StorageProtocol,
86
+ config: StorageConnConfig,
87
+ fn: (storage: StorageClient) => R | Promise<R>,
88
+ ): Promise<R>
89
+ ```
80
90
 
81
- ### StorageFactory
91
+ - 연결 수립 → 콜백 실행 → 연결 종료를 자동 관리
92
+ - 콜백에서 예외가 발생해도 `finally`에서 연결이 안전하게 종료됨
93
+ - 콜백의 반환값을 그대로 반환 (제네릭 `R`)
82
94
 
83
95
  ```typescript
84
- class StorageFactory {
85
- static connect<R>(
86
- type: StorageProtocol,
87
- config: StorageConnConfig,
88
- fn: (storage: StorageClient) => R | Promise<R>,
89
- ): Promise<R>;
90
- }
96
+ // 반환값 활용 예시
97
+ const fileList = await StorageFactory.connect("ftp", config, async (storage) => {
98
+ return await storage.list("/data");
99
+ });
100
+ // fileList: FileInfo[]
91
101
  ```
92
102
 
93
- Factory class that creates protocol-specific clients and manages the connection lifecycle. The `connect` method opens a connection, passes the client to the callback, and guarantees the connection is closed afterwards (even if the callback throws).
103
+ ### StorageClient (공통 인터페이스)
94
104
 
95
- ---
105
+ `FtpStorageClient`와 `SftpStorageClient`가 구현하는 공통 인터페이스. `StorageFactory.connect` 콜백의 `storage` 파라미터 타입이다.
106
+
107
+ | 메서드 | 시그니처 | 설명 |
108
+ |--------|---------|------|
109
+ | `connect` | `(config: StorageConnConfig) => Promise<void>` | 서버 연결. 이미 연결된 인스턴스에서 호출하면 에러 발생 |
110
+ | `close` | `() => Promise<void>` | 연결 종료. 이미 종료된 상태에서 호출해도 안전함 |
111
+ | `mkdir` | `(dirPath: string) => Promise<void>` | 디렉토리 생성 (부모 디렉토리 자동 생성) |
112
+ | `rename` | `(fromPath: string, toPath: string) => Promise<void>` | 파일/디렉토리 이름 변경 |
113
+ | `list` | `(dirPath: string) => Promise<FileInfo[]>` | 디렉토리 내 항목 목록 조회 |
114
+ | `readFile` | `(filePath: string) => Promise<Bytes>` | 원격 파일을 바이트 배열로 읽기 |
115
+ | `exists` | `(filePath: string) => Promise<boolean>` | 파일/디렉토리 존재 여부 확인 |
116
+ | `put` | `(localPathOrBuffer: string \| Bytes, remotePath: string) => Promise<void>` | 로컬 파일 경로 또는 바이트 데이터를 원격 경로에 업로드 |
117
+ | `uploadDir` | `(fromPath: string, toPath: string) => Promise<void>` | 로컬 디렉토리 전체를 원격 경로에 업로드 |
118
+ | `remove` | `(filePath: string) => Promise<void>` | 원격 파일 삭제 |
119
+
120
+ #### 메서드별 주의사항
121
+
122
+ **`exists`**: 부모 디렉토리가 존재하지 않아도 `false` 반환. 네트워크 오류/권한 오류 등 모든 예외에 대해서도 `false` 반환.
123
+ - FTP: `size()` 명령으로 파일 확인 (O(1)), 실패 시 부모 디렉토리 `list()`로 폴백
124
+ - SFTP: `exists()` 내장 메서드 사용 (반환값 `false | 'd' | '-' | 'l'`)
125
+
126
+ **`mkdir`**: 부모 디렉토리가 없으면 재귀적으로 생성한다.
127
+
128
+ **`put`**: 첫 번째 인자로 로컬 파일 경로(string) 또는 바이트 데이터(Bytes)를 받는다.
129
+ - SFTP는 파일 경로 전달 시 `fastPut` 사용 (병렬 전송으로 더 빠름)
130
+
131
+ **`connect`**: 이미 연결된 인스턴스에서 중복 호출하면 `SdError` 발생. 반드시 `close()` 후 재연결해야 한다.
96
132
 
97
133
  ### FtpStorageClient
98
134
 
135
+ FTP/FTPS 프로토콜 클라이언트. 내부적으로 `basic-ftp` 라이브러리를 사용한다.
136
+
99
137
  ```typescript
100
- class FtpStorageClient implements StorageClient {
101
- constructor(secure?: boolean);
102
- }
103
- ```
138
+ import { FtpStorageClient } from "@simplysm/storage";
104
139
 
105
- FTP/FTPS storage client built on [basic-ftp](https://www.npmjs.com/package/basic-ftp). Pass `true` for the `secure` parameter to use FTPS. Using `StorageFactory.connect()` is recommended over direct instantiation.
140
+ const client = new FtpStorageClient(secure?: boolean);
141
+ // secure=false: FTP (기본값)
142
+ // secure=true: FTPS
143
+ ```
106
144
 
107
- ---
145
+ 직접 사용 시 반드시 `connect` → 작업 → `close` 순서를 지켜야 한다. `StorageFactory.connect` 사용을 권장한다.
108
146
 
109
147
  ### SftpStorageClient
110
148
 
149
+ SFTP 프로토콜 클라이언트. 내부적으로 `ssh2-sftp-client` 라이브러리를 사용한다.
150
+
111
151
  ```typescript
112
- class SftpStorageClient implements StorageClient {
113
- }
114
- ```
152
+ import { SftpStorageClient } from "@simplysm/storage";
115
153
 
116
- SFTP storage client built on [ssh2-sftp-client](https://www.npmjs.com/package/ssh2-sftp-client). When no password is provided, authenticates via SSH agent or `~/.ssh/id_ed25519` key file. Using `StorageFactory.connect()` is recommended over direct instantiation.
154
+ const client = new SftpStorageClient();
155
+ ```
117
156
 
118
- ---
157
+ **인증 방식 (자동 선택):**
158
+ 1. `password`가 있으면 비밀번호 인증
159
+ 2. `password`가 없으면 키 기반 인증:
160
+ - `~/.ssh/id_ed25519` 키 파일 + SSH 에이전트(`SSH_AUTH_SOCK`) 시도
161
+ - 키 파싱 실패 시 SSH 에이전트만으로 재시도
119
162
 
120
- ## Usage Examples
163
+ ## 사용 예제
121
164
 
122
- ### Recommended: Using StorageFactory (auto-managed connection)
165
+ ### 파일 업로드/다운로드
123
166
 
124
167
  ```typescript
125
- import { StorageFactory } from "@simplysm/storage";
168
+ await StorageFactory.connect("sftp", config, async (storage) => {
169
+ // 로컬 파일 업로드
170
+ await storage.put("/local/path/file.txt", "/remote/path/file.txt");
126
171
 
127
- // Upload a file via SFTP
128
- await StorageFactory.connect("sftp", { host: "example.com", user: "deploy" }, async (storage) => {
129
- await storage.mkdir("/uploads/2024");
130
- await storage.put("/local/path/file.txt", "/uploads/2024/file.txt");
131
- });
172
+ // 바이트 데이터 직접 업로드
173
+ const data = new TextEncoder().encode("hello");
174
+ await storage.put(data, "/remote/path/hello.txt");
132
175
 
133
- // List files via FTP
134
- const files = await StorageFactory.connect("ftp", { host: "ftp.example.com", user: "admin", password: "secret" }, async (storage) => {
135
- return await storage.list("/data");
176
+ // 파일 읽기
177
+ const content = await storage.readFile("/remote/path/file.txt");
136
178
  });
179
+ ```
180
+
181
+ ### 디렉토리 관리
137
182
 
138
- // Upload a directory via FTPS
139
- await StorageFactory.connect("ftps", { host: "secure.example.com", user: "admin", password: "secret" }, async (storage) => {
140
- await storage.uploadDir("/local/dist", "/remote/dist");
183
+ ```typescript
184
+ await StorageFactory.connect("ftp", config, async (storage) => {
185
+ // 디렉토리 생성 (중첩 경로 자동 생성)
186
+ await storage.mkdir("/remote/a/b/c");
187
+
188
+ // 디렉토리 전체 업로드
189
+ await storage.uploadDir("/local/dist", "/remote/www");
190
+
191
+ // 디렉토리 목록 조회
192
+ const items = await storage.list("/remote/www");
193
+ for (const item of items) {
194
+ // item.name: 이름, item.isFile: 파일 여부
195
+ }
141
196
  });
142
197
  ```
143
198
 
144
- ### Direct client usage
199
+ ### 파일 존재 확인 및 삭제
145
200
 
146
201
  ```typescript
147
- import { SftpStorageClient } from "@simplysm/storage";
148
-
149
- const client = new SftpStorageClient();
150
- await client.connect({ host: "example.com", user: "deploy", password: "secret" });
151
- try {
152
- const exists = await client.exists("/remote/file.txt");
153
- if (exists) {
154
- const data = await client.readFile("/remote/file.txt");
202
+ await StorageFactory.connect("ftps", config, async (storage) => {
203
+ if (await storage.exists("/remote/old-file.txt")) {
204
+ await storage.remove("/remote/old-file.txt");
155
205
  }
156
- await client.put(new TextEncoder().encode("hello"), "/remote/hello.txt");
157
- } finally {
158
- await client.close();
159
- }
206
+
207
+ await storage.rename("/remote/temp.txt", "/remote/final.txt");
208
+ });
209
+ ```
210
+
211
+ ### SSH 키 인증 (SFTP)
212
+
213
+ ```typescript
214
+ // password 미지정 시 자동으로 키 기반 인증
215
+ await StorageFactory.connect("sftp", {
216
+ host: "example.com",
217
+ port: 22,
218
+ user: "deploy",
219
+ // password 생략 → ~/.ssh/id_ed25519 + SSH_AUTH_SOCK 사용
220
+ }, async (storage) => {
221
+ await storage.uploadDir("/local/build", "/var/www/html");
222
+ });
160
223
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/storage",
3
- "version": "13.0.85",
3
+ "version": "13.0.86",
4
4
  "description": "Simplysm Package - Storage Module (node)",
5
5
  "author": "simplysm",
6
6
  "license": "Apache-2.0",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "basic-ftp": "^5.2.0",
23
23
  "ssh2-sftp-client": "^12.1.0",
24
- "@simplysm/core-common": "13.0.85"
24
+ "@simplysm/core-common": "13.0.86"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/ssh2-sftp-client": "^9.0.6"