| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- import libentry, { NativeDirectoryEntry as LibNativeDirectoryEntry, NativeModule as LibNativeModule } from 'libentry.so';
- export interface SmbConnectionOptions {
- host: string;
- share: string;
- username: string;
- password: string;
- domain?: string;
- }
- export interface SmbListOptions extends SmbConnectionOptions {
- path?: string;
- }
- export interface SmbDeleteOptions extends SmbConnectionOptions {
- path: string;
- isDirectory?: boolean;
- }
- export interface SmbCreateOptions extends SmbConnectionOptions {
- path: string;
- }
- export interface SmbRenameOptions extends SmbConnectionOptions {
- path: string;
- newPath: string;
- }
- export interface SmbDirectoryEntry {
- name: string;
- isDirectory: boolean;
- isFile: boolean;
- size: number;
- }
- export type NativeDirectoryEntry = LibNativeDirectoryEntry;
- const bridge: LibNativeModule = libentry as LibNativeModule;
- export class NativeSambaTree {
- private closed = false;
- private readonly treeId: number;
- constructor(treeId: number) {
- this.treeId = treeId;
- }
- private normalizePath(input?: string): string {
- if (!input) {
- return '';
- }
- const cleaned = input.replace(/^[\\/]+/, '').replace(/\\/g, '/');
- return cleaned;
- }
- private ensureOpen(): void {
- if (this.closed) {
- throw new Error('SMB tree has been closed');
- }
- }
- async readDirectory(path?: string): Promise<NativeDirectoryEntry[]> {
- this.ensureOpen();
- const normalized = this.normalizePath(path);
- return bridge.readDirectory(this.treeId, normalized) ?? [];
- }
- deleteEntry(path?: string, isDirectory: boolean = false): void {
- this.ensureOpen();
- const normalized = this.normalizePath(path);
- bridge.deleteEntry(this.treeId, normalized, isDirectory);
- }
- createDirectory(path?: string): void {
- this.ensureOpen();
- const normalized = this.normalizePath(path);
- bridge.createDirectory(this.treeId, normalized);
- }
- renameEntry(path?: string, newPath?: string): void {
- this.ensureOpen();
- const normalized = this.normalizePath(path);
- const normalizedNew = this.normalizePath(newPath);
- bridge.renameEntry(this.treeId, normalized, normalizedNew);
- }
- async close(): Promise<void> {
- if (this.closed) {
- return;
- }
- bridge.disconnectTree(this.treeId);
- this.closed = true;
- }
- }
- export class NativeSambaSession {
- private closed = false;
- private readonly sessionId: number;
- constructor(sessionId: number) {
- this.sessionId = sessionId;
- }
- private ensureOpen(): void {
- if (this.closed) {
- throw new Error('SMB session has been closed');
- }
- }
- async connectTree(share: string): Promise<NativeSambaTree> {
- this.ensureOpen();
- const treeId = Number(bridge.connectTree(this.sessionId, share));
- return new NativeSambaTree(treeId);
- }
- async disconnect(): Promise<void> {
- if (this.closed) {
- return;
- }
- bridge.disconnectSession(this.sessionId);
- this.closed = true;
- }
- }
- export class NativeSambaClient {
- private readonly clientId: number;
- private closed = false;
- constructor(host: string) {
- this.clientId = bridge.createClient(host);
- }
- private ensureOpen(): void {
- if (this.closed) {
- throw new Error('SMB client has been closed');
- }
- }
- async authenticate(options: SmbConnectionOptions): Promise<NativeSambaSession> {
- this.ensureOpen();
- const sessionId = Number(
- bridge.authenticate(this.clientId, options.username, options.password, options.domain)
- );
- return new NativeSambaSession(sessionId);
- }
- async close(): Promise<void> {
- if (this.closed) {
- return;
- }
- bridge.destroyClient(this.clientId);
- this.closed = true;
- }
- }
- async function withSmbTree<T>(options: SmbConnectionOptions, handler: (tree: NativeSambaTree) => Promise<T>): Promise<T> {
- const client = new NativeSambaClient(options.host);
- let session: NativeSambaSession | undefined;
- let tree: NativeSambaTree | undefined;
- try {
- session = await client.authenticate(options);
- tree = await session.connectTree(options.share);
- return await handler(tree);
- } finally {
- await tree?.close();
- await session?.disconnect();
- await client.close();
- }
- }
- export async function listSmbDirectory(options: SmbListOptions): Promise<SmbDirectoryEntry[]> {
- const entries = await withSmbTree(options, (tree: NativeSambaTree) => tree.readDirectory(options.path));
- return entries.map((entry: NativeDirectoryEntry): SmbDirectoryEntry => ({
- name: entry.name ?? entry.fileName ?? 'unknown',
- isDirectory: Boolean(entry.isDirectory),
- isFile: Boolean(entry.isFile),
- size: entry.size ?? 0
- }));
- }
- export async function deleteSmbEntry(options: SmbDeleteOptions): Promise<void> {
- if (!options.path || options.path.length === 0) {
- throw new Error('Remote path must not be empty');
- }
- await withSmbTree(options, async (tree: NativeSambaTree) => {
- tree.deleteEntry(options.path, options.isDirectory === true);
- });
- }
- export async function createSmbDirectory(options: SmbCreateOptions): Promise<void> {
- if (!options.path || options.path.length === 0) {
- throw new Error('Remote path must not be empty');
- }
- await withSmbTree(options, async (tree: NativeSambaTree) => {
- tree.createDirectory(options.path);
- });
- }
- export async function renameSmbEntry(options: SmbRenameOptions): Promise<void> {
- if (!options.path || options.path.length === 0) {
- throw new Error('Remote path must not be empty');
- }
- if (!options.newPath || options.newPath.length === 0) {
- throw new Error('New remote path must not be empty');
- }
- await withSmbTree(options, async (tree: NativeSambaTree) => {
- tree.renameEntry(options.path, options.newPath);
- });
- }
|