Metadata-Version: 2.4
Name: todus-client
Version: 3.0.0
Summary: Cliente S3 profesional con namespaces para el bucket público de ToDus
Author-email: nyxthor-dev <vm1008079@gmail.com>
Maintainer-email: nyxthor-dev <vm1008079@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/nyxthor-dev/todus-client
Project-URL: Repository, https://github.com/nyxthor-dev/todus-client
Project-URL: Issues, https://github.com/nyxthor-dev/todus-client/issues
Project-URL: Documentation, https://github.com/nyxthor-dev/todus-client#readme
Project-URL: Changelog, https://github.com/nyxthor-dev/todus-client/releases
Keywords: s3,todus,cuba,bucket,aws,namespaces,cli,telegram-bot
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: System :: Archiving
Classifier: Topic :: System :: Filesystems
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25
Requires-Dist: tqdm>=4.50
Provides-Extra: yaml
Requires-Dist: pyyaml>=5.0; extra == "yaml"
Provides-Extra: telegram
Requires-Dist: python-telegram-bot>=20.0; extra == "telegram"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Dynamic: license-file

# ToDus Client v3.0

Cliente S3 profesional para el bucket público de ToDus (`s3.todus.cu/stream`),
con abstracción de **namespaces** para aislar espacios por usuario/contenedor.

## Instalación

```bash
cd todus_client
pip install -e .
```

## Uso básico

### CLI

```bash
# Crear namespace
todus ns create alice --description "Cuenta de Alice"

# Listar namespaces
todus ns list

# Subir archivo preservando nombre original
todus upload alice foto.jpg --path fotos/2024 --as "foto vacaciones.jpg"

# Listar archivos
todus list alice
todus list alice fotos --recursive

# Generar enlace de descarga (browser usa el nombre original)
todus share alice fotos/2024/foto.jpg

# Descargar
todus download alice fotos/2024/foto.jpg -o /tmp/

# Sincronizar DB local con bucket S3
todus sync alice --delete-orphans
```

### Python API

```python
from todus import NamespaceManager

manager = NamespaceManager()

# Crear namespace si no existe
if not manager.exists("alice"):
    manager.create("alice", description="Cuenta de Alice")

ns = manager.get_namespace("alice")

# Subir archivo
result = ns.upload("/tmp/foto.jpg", path="fotos",
                   original_name="foto vacaciones.jpg")
print(f"Subido: {result.success}, key: {result.key}")

# Listar archivos
files = ns.list(path="fotos")
for f in files:
    print(f"  {f.key} ({f.size} bytes)")

# Generar link de descarga
link = ns.share_url("fotos/foto.jpg")
print(link.url)

# Descargar (preserva nombre original)
ns.download("fotos/foto.jpg", local_path="/tmp/")
```

## Arquitectura

```
┌─────────────────────────────────────────────────────────────┐
│                    NamespaceManager                         │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Registry (SQLite central: ~/.todus/registry.db)    │   │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐         │   │
│  │  │ alice    │  │  bob     │  │  carol   │         │   │
│  │  └──────────┘  └──────────┘  └──────────┘         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  NamespaceStore (uno por namespace)                 │   │
│  │  ┌──────────────────┐  ┌──────────────────┐         │   │
│  │  │ alice.db         │  │ bob.db           │         │   │
│  │  │  files table     │  │  files table     │         │   │
│  │  └──────────────────┘  └──────────────────┘         │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                  Bucket S3: s3.todus.cu/stream              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  users/alice/fotos/foto.jpg                         │    │
│  │  users/alice/docs/reporte.pdf                       │    │
│  │  users/bob/...                                      │    │
│  │  users/carol/...                                    │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
```

Cada namespace:

- Tiene un prefix único en S3 (`users/<name>/`).
- Mantiene su propia DB SQLite local con metadata (nombre original, content-type, etag, fecha de subida, metadata custom).
- Se aísla de otros namespaces a nivel de path.

## Preparación para bot de Telegram

El diseño está pensado para enchufar un bot de Telegram después:

```python
# En tu bot
from todus import NamespaceManager

manager = NamespaceManager()

def get_user_namespace(user_id: int, username: str):
    ns_name = f"tg_{user_id}"
    if not manager.exists(ns_name):
        manager.create(ns_name, description=f"@{username}")
    return manager.get_namespace(ns_name)

# Cuando un usuario manda un archivo:
async def handle_document(update, context):
    user = update.effective_user
    ns = get_user_namespace(user.id, user.username)

    doc = update.message.document
    file = await doc.get_file()
    local_path = f"/tmp/{doc.file_id}"

    await file.download_to_drive(local_path)
    result = ns.upload(
        local_path=local_path,
        path="telegram",
        original_name=doc.file_name,
        metadata={"from": "telegram", "message_id": update.message.message_id},
    )

    await update.message.reply_text(
        f"Subido: {result.key}\n"
        f"Link: {ns.share_url(result.key).url}"
    )
```

## Comandos disponibles

```
ns create <name> [--description TEXT]       Crear namespace
ns list                                     Listar namespaces
ns info <name>                              Info de namespace
ns delete <name> [--purge] [--delete-s3]    Eliminar namespace
ns sync <name> [--prefix PATH] [--delete-orphans]

upload <ns> <file> [--path DIR] [--as NAME] [--inline] [--dedup]
download <ns> <key> [-o PATH] [--force]
list <ns> [path] [--recursive] [--json] [--names-only]
search <ns> <keyword> [--limit N]
info <ns> <key>
share <ns> <key>
delete <ns> <key>
delete-many <ns> <key1> <key2> ...
move <ns> <src> <dst>
copy <ns> <src> <dst>
rename <ns> <key> <new_name>
mkdir <ns> <path>
tree <ns> [path]
sync <ns> [--prefix PATH] [--delete-orphans]
stats <ns>
shell [namespace]
```
