@simplysm/storage 13.0.96 → 13.0.98

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 +108 -152
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,223 +1,179 @@
1
1
  # @simplysm/storage
2
2
 
3
- FTP, FTPS, SFTP 원격 스토리지 통합 라이브러리. 팩토리 패턴으로 프로토콜에 독립적인 파일 전송을 제공한다.
3
+ Storage Module (node) -- FTP, FTPS, and SFTP storage client with a unified interface and factory pattern.
4
4
 
5
- ## 설치
5
+ ## Installation
6
6
 
7
7
  ```bash
8
8
  npm install @simplysm/storage
9
9
  ```
10
10
 
11
- **의존성:** `basic-ftp` (FTP/FTPS), `ssh2-sftp-client` (SFTP), `@simplysm/core-common`
11
+ ## API Overview
12
12
 
13
- ## 빠른 시작
13
+ ### Types
14
14
 
15
- ```typescript
16
- import { StorageFactory } from "@simplysm/storage";
15
+ | API | Type | Description |
16
+ |-----|------|-------------|
17
+ | `StorageProtocol` | type | Protocol type: `"ftp"`, `"ftps"`, `"sftp"` |
18
+ | `StorageConnConfig` | interface | Connection configuration (host, port, user, password) |
19
+ | `FileInfo` | interface | File entry info (`name`, `isFile`) |
20
+ | `StorageClient` | interface | Unified storage client interface |
17
21
 
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
- ```
22
+ ### Factory
23
+
24
+ | API | Type | Description |
25
+ |-----|------|-------------|
26
+ | `StorageFactory` | class | Factory that creates and manages storage connections |
27
+
28
+ ### Clients
32
29
 
33
- ## 타입 정의
30
+ | API | Type | Description |
31
+ |-----|------|-------------|
32
+ | `FtpStorageClient` | class | FTP/FTPS storage client (basic-ftp) |
33
+ | `SftpStorageClient` | class | SFTP storage client (ssh2-sftp-client) |
34
34
 
35
- ### StorageProtocol
35
+ ## `StorageProtocol`
36
36
 
37
37
  ```typescript
38
38
  type StorageProtocol = "ftp" | "ftps" | "sftp";
39
39
  ```
40
40
 
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` |
46
-
47
- ### StorageConnConfig
41
+ ## `StorageConnConfig`
48
42
 
49
43
  ```typescript
50
44
  interface StorageConnConfig {
51
45
  host: string;
52
- port?: number; // 미지정 시 프로토콜 기본값 사용
46
+ port?: number;
53
47
  user?: string;
54
48
  password?: string;
55
49
  }
56
50
  ```
57
51
 
58
- - **SFTP에서 `password` 미지정 시**: SSH 키 인증으로 전환
59
- 1. `~/.ssh/id_ed25519` 키 파일 자동 탐색
60
- 2. 키 파싱 실패 시 `SSH_AUTH_SOCK` 환경변수의 SSH 에이전트로 재시도
61
-
62
- ### FileInfo
52
+ ## `FileInfo`
63
53
 
64
54
  ```typescript
65
55
  interface FileInfo {
66
- name: string; // 파일/디렉토리 이름
67
- isFile: boolean; // true: 파일, false: 디렉토리
56
+ name: string;
57
+ isFile: boolean;
68
58
  }
69
59
  ```
70
60
 
71
- ### Bytes
72
-
73
- `@simplysm/core-common`에서 정의한 바이트 배열 타입 (`Uint8Array` 기반).
74
-
75
- ## API 레퍼런스
76
-
77
- ### StorageFactory
78
-
79
- 프로토콜에 독립적으로 스토리지 클라이언트를 생성하고 연결을 관리하는 팩토리 클래스.
80
-
81
- #### `StorageFactory.connect<R>(type, config, fn): Promise<R>`
61
+ ## `StorageClient`
82
62
 
83
63
  ```typescript
84
- static async connect<R>(
85
- type: StorageProtocol,
86
- config: StorageConnConfig,
87
- fn: (storage: StorageClient) => R | Promise<R>,
88
- ): Promise<R>
64
+ interface StorageClient {
65
+ connect(config: StorageConnConfig): Promise<void>;
66
+ mkdir(dirPath: string): Promise<void>;
67
+ rename(fromPath: string, toPath: string): Promise<void>;
68
+ list(dirPath: string): Promise<FileInfo[]>;
69
+ readFile(filePath: string): Promise<Bytes>;
70
+ exists(filePath: string): Promise<boolean>;
71
+ put(localPathOrBuffer: string | Bytes, storageFilePath: string): Promise<void>;
72
+ uploadDir(fromPath: string, toPath: string): Promise<void>;
73
+ remove(filePath: string): Promise<void>;
74
+ close(): Promise<void>;
75
+ }
89
76
  ```
90
77
 
91
- - 연결 수립 → 콜백 실행 → 연결 종료를 자동 관리
92
- - 콜백에서 예외가 발생해도 `finally`에서 연결이 안전하게 종료됨
93
- - 콜백의 반환값을 그대로 반환 (제네릭 `R`)
78
+ ## `StorageFactory`
94
79
 
95
80
  ```typescript
96
- // 반환값 활용 예시
97
- const fileList = await StorageFactory.connect("ftp", config, async (storage) => {
98
- return await storage.list("/data");
99
- });
100
- // fileList: FileInfo[]
81
+ class StorageFactory {
82
+ static async connect<R>(
83
+ type: StorageProtocol,
84
+ config: StorageConnConfig,
85
+ fn: (storage: StorageClient) => R | Promise<R>,
86
+ ): Promise<R>;
87
+ }
101
88
  ```
102
89
 
103
- ### StorageClient (공통 인터페이스)
104
-
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()` 후 재연결해야 한다.
90
+ Creates a storage connection, executes the callback, and automatically closes the connection. The connection is closed even if the callback throws an exception. This is the recommended way to use storage clients.
132
91
 
133
- ### FtpStorageClient
134
-
135
- FTP/FTPS 프로토콜 클라이언트. 내부적으로 `basic-ftp` 라이브러리를 사용한다.
92
+ ## `FtpStorageClient`
136
93
 
137
94
  ```typescript
138
- import { FtpStorageClient } from "@simplysm/storage";
139
-
140
- const client = new FtpStorageClient(secure?: boolean);
141
- // secure=false: FTP (기본값)
142
- // secure=true: FTPS
95
+ class FtpStorageClient implements StorageClient {
96
+ constructor(secure?: boolean);
97
+ async connect(config: StorageConnConfig): Promise<void>;
98
+ async mkdir(dirPath: string): Promise<void>;
99
+ async rename(fromPath: string, toPath: string): Promise<void>;
100
+ async list(dirPath: string): Promise<FileInfo[]>;
101
+ async readFile(filePath: string): Promise<Bytes>;
102
+ async exists(filePath: string): Promise<boolean>;
103
+ async put(localPathOrBuffer: string | Bytes, storageFilePath: string): Promise<void>;
104
+ async uploadDir(fromPath: string, toPath: string): Promise<void>;
105
+ async remove(filePath: string): Promise<void>;
106
+ close(): Promise<void>;
107
+ }
143
108
  ```
144
109
 
145
- 직접 사용 반드시 `connect` 작업 `close` 순서를 지켜야 한다. `StorageFactory.connect` 사용을 권장한다.
146
-
147
- ### SftpStorageClient
110
+ FTP/FTPS storage client. The `secure` constructor parameter controls FTPS mode. Use `StorageFactory.connect` instead of direct usage for automatic connection lifecycle management.
148
111
 
149
- SFTP 프로토콜 클라이언트. 내부적으로 `ssh2-sftp-client` 라이브러리를 사용한다.
112
+ ## `SftpStorageClient`
150
113
 
151
114
  ```typescript
152
- import { SftpStorageClient } from "@simplysm/storage";
153
-
154
- const client = new SftpStorageClient();
115
+ class SftpStorageClient implements StorageClient {
116
+ async connect(config: StorageConnConfig): Promise<void>;
117
+ async mkdir(dirPath: string): Promise<void>;
118
+ async rename(fromPath: string, toPath: string): Promise<void>;
119
+ async list(dirPath: string): Promise<FileInfo[]>;
120
+ async readFile(filePath: string): Promise<Bytes>;
121
+ async exists(filePath: string): Promise<boolean>;
122
+ async put(localPathOrBuffer: string | Bytes, storageFilePath: string): Promise<void>;
123
+ async uploadDir(fromPath: string, toPath: string): Promise<void>;
124
+ async remove(filePath: string): Promise<void>;
125
+ async close(): Promise<void>;
126
+ }
155
127
  ```
156
128
 
157
- **인증 방식 (자동 선택):**
158
- 1. `password`가 있으면 비밀번호 인증
159
- 2. `password`가 없으면 키 기반 인증:
160
- - `~/.ssh/id_ed25519` 키 파일 + SSH 에이전트(`SSH_AUTH_SOCK`) 시도
161
- - 키 파싱 실패 시 SSH 에이전트만으로 재시도
129
+ SFTP storage client. Supports password authentication and SSH key/agent authentication. Use `StorageFactory.connect` instead of direct usage for automatic connection lifecycle management.
162
130
 
163
- ## 사용 예제
131
+ ## Usage Examples
164
132
 
165
- ### 파일 업로드/다운로드
133
+ ### Upload files via StorageFactory (recommended)
166
134
 
167
135
  ```typescript
168
- await StorageFactory.connect("sftp", config, async (storage) => {
169
- // 로컬 파일 업로드
170
- await storage.put("/local/path/file.txt", "/remote/path/file.txt");
171
-
172
- // 바이트 데이터 직접 업로드
173
- const data = new TextEncoder().encode("hello");
174
- await storage.put(data, "/remote/path/hello.txt");
175
-
176
- // 파일 읽기
177
- const content = await storage.readFile("/remote/path/file.txt");
178
- });
179
- ```
180
-
181
- ### 디렉토리 관리
136
+ import { StorageFactory } from "@simplysm/storage";
182
137
 
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
- }
138
+ await StorageFactory.connect("sftp", {
139
+ host: "example.com",
140
+ user: "deploy",
141
+ password: "secret",
142
+ }, async (storage) => {
143
+ await storage.mkdir("/var/www/app");
144
+ await storage.put("/local/dist/bundle.js", "/var/www/app/bundle.js");
196
145
  });
146
+ // Connection is automatically closed
197
147
  ```
198
148
 
199
- ### 파일 존재 확인 및 삭제
149
+ ### List remote directory
200
150
 
201
151
  ```typescript
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");
205
- }
152
+ import { StorageFactory } from "@simplysm/storage";
206
153
 
207
- await storage.rename("/remote/temp.txt", "/remote/final.txt");
154
+ const files = await StorageFactory.connect("ftp", {
155
+ host: "files.example.com",
156
+ user: "admin",
157
+ password: "pass",
158
+ }, async (storage) => {
159
+ return await storage.list("/uploads");
208
160
  });
161
+
162
+ for (const file of files) {
163
+ // file.name, file.isFile
164
+ }
209
165
  ```
210
166
 
211
- ### SSH 인증 (SFTP)
167
+ ### Upload entire directory via FTPS
212
168
 
213
169
  ```typescript
214
- // password 미지정 자동으로 키 기반 인증
215
- await StorageFactory.connect("sftp", {
216
- host: "example.com",
217
- port: 22,
170
+ import { StorageFactory } from "@simplysm/storage";
171
+
172
+ await StorageFactory.connect("ftps", {
173
+ host: "secure.example.com",
218
174
  user: "deploy",
219
- // password 생략 → ~/.ssh/id_ed25519 + SSH_AUTH_SOCK 사용
175
+ password: "secret",
220
176
  }, async (storage) => {
221
- await storage.uploadDir("/local/build", "/var/www/html");
177
+ await storage.uploadDir("/local/dist", "/remote/app");
222
178
  });
223
179
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/storage",
3
- "version": "13.0.96",
3
+ "version": "13.0.98",
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.96"
24
+ "@simplysm/core-common": "13.0.98"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/ssh2-sftp-client": "^9.0.6"