Metadata-Version: 2.4
Name: edrive-anyshare
Version: 1.0.0
Summary: AnyShare eDrive login, directory, upload, and sharing client
Project-URL: Documentation, https://developers.aishutech.com/napi/documents/307
Keywords: anyshare,edrive,file-sharing,enterprise-content-management
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Internet
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# AnyShare eDrive Python package

This is a tenant-neutral Python client for AnyShare and eDrive deployments.
The server URL, account, password, and optional browser-login public key are
provided by the application, so the package is not tied to one provider or
domain.

It supports:

- browser-style AnyShare login
- reusable cookie-based sessions
- owned document-library and directory operations
- remote path resolution and directory creation
- recursive folder uploads
- permanent anonymous share-link reuse or creation
- authenticated requests to other supported AnyShare endpoints

The package uses the browser-login and legacy document/share endpoint shapes
implemented by the reference client. AnyShare deployments can expose
different features or endpoint versions; confirm the API contract for the
target tenant before enabling write operations.

## Requirements

- Python 3.9 or newer
- curl
- openssl

There are no third-party runtime dependencies. HTTPS connections use the
system curl installation and its certificate store.

## Install

Install the published distribution:

```bash
python -m pip install edrive-anyshare
```

Install this source tree for development:

```bash
cd /path/to/edrive
python -m pip install -e .
```

The import name is edrive and the distribution name is edrive-anyshare.

## Configuration

The library accepts credentials explicitly and does not require or load an
environment file. This avoids silently selecting credentials or a tenant in a
library process.

For a shell-based application:

```bash
export EDRIVE_BASE_URL=https://anyshare.example.com
export EDRIVE_USERNAME=your-account
export EDRIVE_PASSWORD=your-password
```

An application may read these optional variables with its own configuration
system and pass them to login:

```python
import os

from edrive import login

with login(
    os.environ["EDRIVE_USERNAME"],
    os.environ["EDRIVE_PASSWORD"],
    os.environ["EDRIVE_BASE_URL"],
) as session:
    print(session.username)
```

The base URL must be an absolute http or https URL. Trailing slashes are
removed automatically, and query strings or fragments are rejected.

## Quick start: upload a folder

```python
from edrive import ONDUP_OVERWRITE, login, upload_folder

with login(
    "your-account",
    "your-password",
    "https://anyshare.example.com",
) as session:
    result = upload_folder(
        session,
        "/path/to/local/folder",
        "Documents/Reports",
        ondup=ONDUP_OVERWRITE,
        create_share_link=True,
    )
    print(result.share_url)
```

The first component of a remote path is an owned document-library name.
Missing nested folders are created by default. Pass remote_docid instead of
remote_path to skip library and path lookup:

```python
result = upload_folder(
    session,
    "/path/to/local/folder",
    remote_docid="gns://library/folder-id",
    create_share_link=False,
)
```

If both remote_path and remote_docid are supplied, remote_docid takes
precedence. The default duplicate policy is ONDUP_OVERWRITE; use
ONDUP_RENAME to keep both files.

## Complete demonstration

The following script does not use an environment file. It asks for the tenant
URL, account, and password, lists the account's document libraries, and asks
for confirmation before uploading a local folder:

```python
from getpass import getpass
from pathlib import Path

from edrive import (
    ONDUP_OVERWRITE,
    list_owned_doc_libs,
    login,
    upload_folder,
)

base_url = input("AnyShare URL: ").strip()
username = input("Account: ").strip()
password = getpass("Password: ")
local_folder = Path(input("Local folder: ").strip()).expanduser()
remote_path = input("Remote path, for example Documents/Reports: ").strip()

with login(username, password, base_url) as session:
    libraries = list_owned_doc_libs(session)
    print("Owned document libraries:")
    for library in libraries:
        print(" -", library.get("name"), library.get("id"))

    if input("Upload this folder? [y/N] ").strip().lower() == "y":
        result = upload_folder(
            session,
            local_folder,
            remote_path,
            ondup=ONDUP_OVERWRITE,
            create_share_link=True,
        )
        print("Uploaded files:", len(result.uploaded_files))
        print("Share URL:", result.share_url or "(not created)")
```

For a read-only connection check, stop after list_owned_doc_libs. Uploading
and share-link creation are write operations and depend on tenant permissions.

## Authentication

### login

```python
login(
    username,
    password,
    base_url,
    *,
    cookiejar=None,
    login_public_key=None,
)
```

Returns an EdriveSession. The session follows the AnyShare web login
redirects, submits the CSRF/challenge login payload, encrypts the password
with RSA through openssl, and reads the OAuth token from the cookie jar.

Use the session as a context manager. A temporary cookie jar is deleted when
the context closes. A caller-provided cookie jar is retained on disk, but the
session object clears its reference after close:

```python
with login(
    "your-account",
    "your-password",
    "https://anyshare.example.com",
    cookiejar="/tmp/anyshare.cookies",
) as session:
    pass
```

The package exports LOGIN_PUBLIC_KEY as the bundled default public key:

```python
from edrive import LOGIN_PUBLIC_KEY
```

If a tenant uses a different browser-login key, provide its PEM contents:

```python
from pathlib import Path

from edrive import login

tenant_key = Path("/secure/path/tenant-login-public-key.pem").read_text()

with login(
    "your-account",
    "your-password",
    "https://anyshare.example.com",
    login_public_key=tenant_key,
) as session:
    pass
```

The public key is not a password and does not need to be kept secret. Do not
commit private keys, passwords, cookie jars, or real credentials.

## Browse document libraries and folders

```python
from edrive import (
    create_dir,
    find_child_dir,
    list_dir,
    list_owned_doc_libs,
    login,
    resolve_docid_by_name,
    resolve_folder_path,
)

with login("user", "password", "https://anyshare.example.com") as session:
    libraries = list_owned_doc_libs(session)
    library_id = resolve_docid_by_name(session, "Documents")

    listing = list_dir(session, library_id)
    print(listing.get("dirs", []))
    print(listing.get("files", []))

    reports = find_child_dir(session, library_id, "Reports")
    if reports is None:
        reports = create_dir(session, library_id, "Reports")

    folder_id, parent_id, created = resolve_folder_path(
        session,
        "Documents/Reports/2026",
        create=True,
    )
    print(folder_id, parent_id, created)
```

Directory and library responses are returned as dictionaries containing the
fields supplied by the tenant. Document IDs may be ordinary IDs or AnyShare
URI-style values.

## Upload results

upload_folder returns UploadResult:

```python
result.share_url
result.share_id
result.share_link_created
result.local_path
result.remote_path
result.remote_folder_name
result.remote_folder_docid
result.remote_parent_docid
result.uploaded_files
result.created_dirs
```

Use result.to_dict() when the result must be serialized as JSON:

```python
payload = result.to_dict()
```

The upload process uses the AnyShare begin-upload, direct multipart upload,
and end-upload sequence. If an upload fails after a remote folder has been
created, the client does not delete that remote content automatically.

## Share links

```python
from edrive import (
    create_anonymous_share_link,
    get_or_create_permanent_share_link,
    list_share_links,
)

links = list_share_links(session, "gns://library/folder-id")

link_id = create_anonymous_share_link(
    session,
    "gns://library/folder-id",
    title="Reports",
    allow=["display", "preview", "download"],
)

share = get_or_create_permanent_share_link(
    session,
    "gns://library/folder-id",
    title="Reports",
)
print(share["url"], share["created"])
```

The helper reuses a permanent link when one already exists. A tenant may
return HTTP 202 when link creation requires approval; this is reported as a
typed error.

## Authenticated API requests

Use api_request for an endpoint not wrapped by a convenience function:

```python
from edrive import api_request

status, data = api_request(
    session,
    "GET",
    "/api/efast/v1/owned-doc-lib",
)
if status == 200:
    print(data)
```

The function:

- accepts relative paths or absolute http(s) URLs
- adds the session Bearer token when present
- encodes json_body as JSON
- returns (status, decoded_data)
- returns text when the response is not JSON
- returns None for an empty response

The exported default endpoint prefixes are API_PREFIX and
SHARE_LINK_PREFIX. They are kept in one module so tenant-specific endpoint
adaptations can be made without changing the public operation signatures.

## Errors

All client-specific errors inherit from EdriveError, which inherits from
RuntimeError:

```python
from edrive import (
    EdriveAuthenticationError,
    EdriveError,
    EdriveHTTPError,
    EdriveProtocolError,
    EdriveTransportError,
    EdriveUploadError,
)

try:
    with login("user", "password", "https://anyshare.example.com") as session:
        pass
except EdriveAuthenticationError:
    print("The tenant rejected the login.")
except EdriveTransportError:
    print("curl or the network was unavailable.")
except EdriveHTTPError:
    print("AnyShare returned an unsuccessful status.")
except EdriveProtocolError:
    print("The tenant response did not match the expected shape.")
except EdriveUploadError:
    print("The direct upload failed.")
except EdriveError:
    print("Another eDrive client error occurred.")
```

Passwords, access tokens, authorization values, cookies, and common login
fields are redacted from client-generated error messages. The library does
not configure application logging.
