Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/tentwentyfour/nextcloud-link
Javascript/Typescript client that communicates with Nextcloud's WebDAV and OCS APIs
https://github.com/tentwentyfour/nextcloud-link
javascript nextcloud nodejs ocs typescript webdav
Last synced: about 2 months ago
JSON representation
Javascript/Typescript client that communicates with Nextcloud's WebDAV and OCS APIs
- Host: GitHub
- URL: https://github.com/tentwentyfour/nextcloud-link
- Owner: tentwentyfour
- License: mit
- Created: 2018-04-11T13:23:19.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2023-12-06T10:40:36.000Z (about 1 year ago)
- Last Synced: 2024-12-18T00:27:05.263Z (about 2 months ago)
- Topics: javascript, nextcloud, nodejs, ocs, typescript, webdav
- Language: TypeScript
- Homepage:
- Size: 3.67 MB
- Stars: 57
- Watchers: 13
- Forks: 7
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# nextcloud-link ![npm](https://img.shields.io/npm/v/nextcloud-link?label=version)
![](https://github.com/tentwentyfour/nextcloud-link/workflows/Node.js%20CI/badge.svg)
![Snyk Vulnerabilities for GitHub Repo](https://img.shields.io/snyk/vulnerabilities/github/tentwentyfour/nextcloud-link)
[![NPM Downloads](https://img.shields.io/npm/dt/nextcloud-link.svg?style=flat)](https://npmjs.org/package/nextcloud-link)
[![Greenkeeper badge](https://badges.greenkeeper.io/tentwentyfour/nextcloud-link.svg)](https://greenkeeper.io/)
![GitHub](https://img.shields.io/github/license/tentwentyfour/nextcloud-link?color=blue)
![Twitter Follow](https://img.shields.io/twitter/follow/1024Lu?label=Follow%20TenTwentyFour&style=social)> Node.js client to interact with [Nextcloud](https://nextcloud.com), developed with :hearts: by [TenTwentyFour](https://tentwentyfour.lu).
![tiny persons handling files in a huge directory](./cloud.png "Directory")
## Table of Contents
- [Getting Started](#getting-started)
- [Features](#features)
- [Interface](#interface)
- [Core](#core)
- [Activities](#activities)
- [Users](#users)
- [Groups](#groups)
- [Shares](#shares)
- [Groupfolders](#groupfolders)
- [Exceptions](#exceptions)
- [Types](#types)
- [Helpers](#helpers)
- [Definitions](#definitions)
- [Contributing](#contributing)## Getting started
If you're not planning on contributing code to the project, you can simply install `nextcloud-link` to your project by running:
`npm install --save nextcloud-link`
### Quick-Start
Establishing a connection from your ECMA- or TypeScript project to a Nextcloud instance can be done like this:
```TypeScript
import NextcloudClient from 'nextcloud-link';const client = new NextcloudClient({
"url": "https://my.nextcloud.com",
"password": "useSomeBetterPassphraseThanThis",
"username": "cloudrider",
});
```Once you have initiated the connection to your nextcloud instance, it is generally a good idea to delay any file or OCS operations until the connection to your instance has been established and verified. Using the `client` object from above, we can do this like so:
```TypeScript
while (true) {
if (await client.checkConnectivity()) {
return;
}await new Promise(resolve => setTimeout(resolve, 5000));
}
```In a real set-up, you'll probably want to limit the number of tries to something sensible, like 15 to 30 seconds by throwing after a given number of attempts.
Finally, use any of the methods described below to interact with your Nextcloud instance:
```TypeScript
const uploader = await client.getCreatorByPath('/Nextcloud.png');
```## Features
- :link: Interacts with Nextcloud instances via the WebDAV protocol
- :rocket: Allows the use of streams for file transfer
- :pray: Asserts Nextcloud connectivity before attempting any requests
- :tada: OCS methods for groups, users, shares, activity, and groupfolders## Interface
### Core
The following methods are available on `client`:
`configureWebdavConnection(options: ConnectionOptions): void`
> Configures the Nextcloud connection to talk to a specific Nextcloud WebDav endpoint. This does not issue any kind of request, so it doesn't throw if the parameters are incorrect. This merely sets internal variables.`checkConnectivity(): Promise`
> Checks whether the connection to the configured WebDav endpoint succeeds. This does not throw, it consistently returns a Promise wrapping a boolean.`pipeStream(path: string, stream: Stream.Readable): Promise`
> Deprecated, will be removed in version 2, use uploadFromStream`uploadFromStream(targetPath: string, stream: Stream.Readable): Promise`
> Saves the data obtained through `stream` to the Nextcloud instance at `path`. Throws a `NotFoundError` if the requested path does not exist.`downloadToStream(sourcePath: string, stream: Stream.Readable): Promise`
> Pipes the data obtained by reading a file at `path` on the Nextcloud instance to the provided local `stream`. Throws a `NotFoundError` if the requested path does not exist.`as(username: string, password: string): NextcloudClient`
> Creates a copy of the client that runs the request as the user with the passed credentials. This does absolutely no verification, so you should use `checkConnectivity` to verify the credentials.`createFolderHierarchy(path: string): Promise`
> This is basically a recursive `mkdir`.`put(path: string, content: Webdav.ContentType): Promise`
> This saves a Webdav.ContentType at `path`. Throws a `NotFoundError` if the path to the requested directory does not exist.`rename(fromFullPath: string, toFileName: string): Promise`
> This allows to rename files or directories.`move(fromFullPath: string, toFullPath: string): Promise`
> This allows to move files or entire directories.`getWriteStream(path: string): Promise`
> Gets a write stream to a remote Nextcloud `path`. Throws a `NotFoundError` if the path to the requested directory does not exist.`getReadStream(path: string): Promise`
> Gets a read stream to a remote Nextcloud `path`.`getFolderProperties(path: string, extraProperties?: FileDetailProperty[]): Promise`
> Retrieves properties for the folder. Use [extraProperties](#webdav-extraproperties) to request properties not returned by default.`touchFolder(path: string): Promise`
> Smart `mkdir` implementation that doesn't complain if the folder at `path` already exists.`getFiles(path: string): Promise`
> List files in a directory.`getFolderFileDetails(path: string, extraProperties?: FileDetailProperty[]): Promise`
> Same as `getFiles`, but returns more details instead of just file names. Use extraProperties to request properties not returned by default.`remove(path: string): Promise`
> Removes file or directories. Does not complain if directories aren't empty.`exists(path: string): Promise`
> Simple test that checks whether a file or directory exists. This indicates it in the return value, not by throwing exceptions.`get(path: string): Promise`
> Gets a file as a string/Buffer.`getCreatorByPath(path: string): Promise`
> Gets the username of the user that created the file or folder.`getCreatorByFileId(fileId: number|string): Promise`
> Gets the username of the user that created the file or folder.### Activities
The following methods are available on `client.activities``get(fileId: number|string, sort?: 'asc'|'desc', limit?: number, sinceActivityId?: number): Promise`
> Returns all activities belonging to a file or folder. Use the `limit` argument to override the server-default.### Users
The following methods are available on `client.users`:`removeSubAdminFromGroup(userId: string, groupId: string): Promise`
> Remove a user as a Sub Admin from a group.`addSubAdminToGroup(userId: string, groupId: string): Promise`
> Add a user as a Sub Admin to a group.`resendWelcomeEmail(userId: string): Promise`
> Resend the Welcome email to a user.`removeFromGroup(userId: string, groupId: string): Promise`
> Remove a user from a group.`getSubAdminGroups(userId: string): Promise`
> Gets a list of all the groups a user is a Sub Admin of.`setEnabled(userId: string, isEnabled: boolean): Promise`
> Enables or disables a user.`addToGroup(userId: string, groupId: string): Promise`
> Add a user to a group.`getGroups(userId: string): Promise`
> Gets a list of all the groups a user is a member of.`delete(userId: string): Promise`
> Delete a user.`edit(userId: string, field: OcsEditUserField, value: string): Promise`
> Edit a single field of a user.`list(search?: string, limit?: number, offset?: number): Promise`
> Gets a list of all users. Use the `limit` argument to override the server-default.`add(user: OcsNewUser): Promise`
> Add a new user.`get(userId: string): Promise`
> Gets the user information.### Groups
The following methods are available on `client.groups`:`getSubAdmins(groupId: string): Promise`
> Gets a list of all the users that are a Sub Admin of the group.`getUsers(groupId: string): Promise`
> Gets a list of all the users that are a member of the group.`delete(groupId: string): Promise`
> Delete a group.`list(search?: string, limit?: number, offset?: number): Promise`
> Gets a list of all groups.
Use the `limit` argument to override the server-default.`add(groupId: string): Promise`
> Add a new group.### Shares
The following methods are available on `client.shares`:`delete(shareId: string| number): Promise`
> Delete a share.`list(path?: string, includeReshares?: boolean, showForSubFiles?: boolean): Promise`
> Gets a list of all the shares. Use `path` to show all the shares for that specific file or folder. Use `includeReshares` to also include shares not belonging to the user. Use `showForSubFiles` to show the shares of the children instead. This will throw an error if the path is a file.`add: (path: string, shareType: OcsShareType, shareWith?: string, permissions?: OcsSharePermissions, password?: string, publicUpload?: boolean): Promise`
> Add a new share. `shareWith` has to be filled if `shareType` is a `user` or `group`. Use `permissions` bit-wise to add several permissions. `OcsSharePermissions.default` will let the server decide the permissions. This will throw an error if the specific share already exists. Use `shares.edit` to edit an existing share.`get: (shareId: string|number): Promise`
> Gets the share information.#### edit
The following methods are available on `client.shares.edit`:`permissions(shareId: string|number, permissions: OcsSharePermissions): Promise`
> Change the permissions. Use `permissions` bit-wise to add several permissions.`password(shareId: string|number, password: string): Promise`
> Change the password. Only `OcsShareType.publicLink` uses passwords.`publicUpload(shareId: string|number, isPublicUpload: boolean): Promise`
> Enable / disable public upload for public shares.`expireDate(shareId: string|number, expireDate: string): Promise`
> Add an expire date to the share. If the expire date is in the past, Nextcloud will remove the share.`note(shareId: string|number, note: string): Promise`
> Add a note to the share.### Groupfolders
To be able to use `groupfolders` interface, the [groupfolders](https://github.com/nextcloud/groupfolders) app needs to be downloaded and activated in the Nextcloud settings.
The following methods are available on `client.groupfolders`:`getFolders: () => Promise`
> Returns a list of all configured folders and their settings.`getFolder: (fid: number) => Promise`
> Return a specific configured groupfolder and its settings, `null` if not found.`addFolder: (mountpoint: string) => Promise`
> Create a new groupfolder with name `mountpoint` and returns its `id`.`removeFolder: (fid: number) => Promise`
> Delete a groupfolder. Returns `true` if successful (even if the groupfolder didn't exist).`addGroup: (fid: number, gid: string) => Promise`
> Give a group access to a groupfolder.`removeGroup: (fid: number, gid: string) => Promise`
> Remove access from a group to a groupfolder.`setPermissions: (fid: number, gid: string, permissions: number) => Promise`
> Set the permissions a group has in a groupfolder. The `permissions` parameter is a bitmask of [permissions constants](https://github.com/nextcloud/server/blob/b4f36d44c43aac0efdc6c70ff8e46473341a9bfe/lib/public/Constants.php#L65).`enableACL: (fid: number, enable: boolean) => Promise`
> Enable/Disable groupfolder advanced permissions.`setManageACL: (fid: number, type: 'group' | 'user', id: string, manageACL: boolean) => Promise`
> Grants/Removes a group or user the ability to manage a groupfolders' advanced permissions.
> `mappingId`: the id of the group/user to be granted/removed access to/from the groupfolder
> `mappingType`: 'group' or 'user'
> `manageAcl`: true to grants ability to manage a groupfolders' advanced permissions, false to remove`setQuota: (fid: number, quota: number) => Promise`
> Set the `quota` for a groupfolder in bytes (use `-3` for unlimited).`renameFolder: (fid: number, mountpoint: string) => Promise`
> Change the name of a groupfolder to `mountpoint`.Note: If the `groupfolders` app is not activated, the requests are returning code `302`. The GET requests are redirected to the Location header (`/apps/dashboard/`) which makes it complicated to catch (returns `200` and `text/html` content type). The `client.groupfolders` methods would then throw with an error code `500` and a message "Unable to parse the response body as valid JSON".
## Exceptions
### NotFoundError
Error indicating that the requested resource doesn't exist, or that the path leading to it doesn't exist in the case of writes.### ForbiddenError
Error indicating that Nextcloud denied the request.### NextcloudError
Generic wrapper for the HTTP errors returned by Nextcloud.### OcsError
Errors used by all OCS calls.
It will return the reason why a request failed as well as a status code if it is available.## Types
### ConnectionOptions
```javascript
interface ConnectionOptions {
url: string;
username?: string;
password?: string;
}
```### WebDAV
```javascript
interface FileDetails {
creationDate?: Date;
lastModified: Date;
href: string;
name: string;
size: number;
isDirectory: boolean;
isFile: boolean;
type: 'directory' | 'file';
}
```### OCS
```javascript
interface OcsActivity {
activityId: number;
app: string;
type: string;
user: string;
subject: string;
subjectRich: [];
message: string;
messageRich: [];
objectType: string;
fileId: number;
objectName: string;
objects: {};
link: string;
icon: string;
datetime: Date;
}interface OcsUser {
id: string;
enabled: boolean;
lastLogin: number;
email: string;
displayname: string;
phone: string;
address: string;
website: string;
twitter: string;
groups: string[];
language: string;
locale: string;
}interface OcsNewUser {
userid: string;
password?: string;
email?: string;
displayName?: string;
groups?: string[];
subadmin?: string[];
quota?: number;
language?: string;
}type OcsEditUserField =
'password' |
'email' |
'displayname' |
'quota' |
'phone' |
'address' |
'website' |
'twitter' |
'locale' |
'language' ;enum OcsShareType {
user = 0,
group = 1,
publicLink = 3,
federatedCloudShare = 6,
}enum OcsSharePermissions {
default = -1,
read = 1,
update = 2,
create = 4,
delete = 8,
share = 16,
all = 31,
}interface OcsShare {
id: number;
shareType: OcsShareType;
shareTypeSystemName: string;
ownerUserId: string;
ownerDisplayName: string;
permissions: OcsSharePermissions;
permissionsText: string;
sharedOn: Date;
sharedOnTimestamp: number;
parent: string;
expiration: Date;
token: string;
fileOwnerUserId: string;
fileOwnerDisplayName: string;
note: string;
label: string;
path: string;
itemType: 'file' | 'folder';
mimeType: string;
storageId: string;
storage: number;
fileId: number;
parentFileId: number;
fileTarget: string;
sharedWith: string;
sharedWithDisplayName: string;
mailSend: boolean;
hideDownload: boolean;
password?: string;
sendPasswordByTalk?: boolean;
url?: string;
}type OcsEditShareField =
'permissions' |
'password' |
'expireDate' |
'note' ;interface OcsGroupfolderManageRule {
type: 'group' | 'user'
id: string;
displayname: string;
}interface OcsGroupfolder {
id: number;
mountPoint: string;
groups: Record;
quota: number;
size: number;
acl: boolean;
manage?: OcsGroupfolderManageRule[];
}
```## Helpers
`createFileDetailProperty(namespace: string, namespaceShort: string, element: string, nativeType?: boolean, defaultValue?: any): FileDetailProperty`
> Creates a FileDetailProperty filled in with the supplied arguments, which can be used when using getFolderFileDetails.`createOwnCloudFileDetailProperty(element: string, nativeType?: boolean, defaultValue?: any): FileDetailProperty`
> Uses createFileDetailProperty to request an OwnCloud property.`createNextCloudFileDetailProperty(element:string, nativeType?: boolean, defaultValue?: any): FileDetailProperty`
> Uses createFileDetailProperty to request a Nextcloud property.## Definitions
### fileId
This is an OwnCloud property representing either a File or a Folder. It is own of the so-called `extraProperties` only returned by the `WebDAV` on request. See the following section for more details
on `extraProperties`.### WebDAV extraProperties
`extraProperties` is an optional parameter that can be passed to both [`getFolderProperties`](#getfolderproperties) and [`getFolderFileDetails`](#getfolderfiledetails). The parameter consists of a list of optional properties that are not returned by the `WebDAV` interface by default.
A simple example that requests the `fileId` of a directory on top of the standard properties returned by the `WebDAV` API would be:
```typescript
const fileId = createOwnCloudFileDetailProperty('fileid', true);
const documentList = await client.getFolderFileDetails('/Documents', [fileId]);
for (const directory of documentList) {
const folderId = directory.extraProperties.fileid;
}
```
Which properties get returned by default and which are only available at request can be found in the [Nextcloud Documentation](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/WebDAV/basic.html#requesting-properties).### Sub Admin
This is a Nextcloud term used to describe a user that has administrator rights for a group.
## Contributing
Running tests is a little complicated right now, we're looking into improving this situation. While you can initiate tests using a normal `npm test`, you'll require `docker` and `docker-compose` to be installed in your path.