caldav-adapter 4.3.1 → 5.0.0

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 (93) hide show
  1. package/.commitlintrc.js +3 -0
  2. package/.editorconfig +9 -0
  3. package/.gitattributes +1 -0
  4. package/.github/workflows/ci.yml +24 -0
  5. package/.husky/commit-msg +4 -0
  6. package/.husky/pre-commit +4 -0
  7. package/.lintstagedrc.js +5 -0
  8. package/.prettierrc.js +5 -0
  9. package/.remarkignore +1 -0
  10. package/.remarkrc.js +3 -0
  11. package/.xo-config.js +8 -0
  12. package/LICENSE +21 -0
  13. package/README.md +26 -240
  14. package/common/parse-body.js +16 -0
  15. package/common/response.js +51 -0
  16. package/common/tags.js +412 -0
  17. package/common/winston.js +32 -0
  18. package/common/x-build.js +109 -0
  19. package/common/xml.js +36 -0
  20. package/index.js +171 -0
  21. package/package.json +58 -40
  22. package/routes/calendar/calendar/calendar-multiget.js +48 -0
  23. package/routes/calendar/calendar/calendar-query.js +80 -0
  24. package/routes/calendar/calendar/delete.js +40 -0
  25. package/routes/calendar/calendar/event-response.js +37 -0
  26. package/routes/calendar/calendar/expand-property.js +11 -0
  27. package/routes/calendar/calendar/get.js +27 -0
  28. package/routes/calendar/calendar/propfind.js +73 -0
  29. package/routes/calendar/calendar/proppatch.js +32 -0
  30. package/routes/calendar/calendar/put.js +76 -0
  31. package/routes/calendar/calendar/report.js +36 -0
  32. package/routes/calendar/calendar/sync-collection.js +36 -0
  33. package/routes/calendar/calendar.js +80 -0
  34. package/routes/calendar/user/propfind.js +60 -0
  35. package/routes/calendar/user/proppatch.js +31 -0
  36. package/routes/principal/principal.js +31 -0
  37. package/routes/principal/propfind.js +32 -0
  38. package/routes/principal/report.js +27 -0
  39. package/test/test.js +8 -0
  40. package/lib/common/date.d.ts +0 -2
  41. package/lib/common/date.js +0 -11
  42. package/lib/common/eventBuild.d.ts +0 -6
  43. package/lib/common/eventBuild.js +0 -170
  44. package/lib/common/parseBody.d.ts +0 -2
  45. package/lib/common/parseBody.js +0 -21
  46. package/lib/common/response.d.ts +0 -6
  47. package/lib/common/response.js +0 -53
  48. package/lib/common/tags.d.ts +0 -263
  49. package/lib/common/tags.js +0 -337
  50. package/lib/common/winston.d.ts +0 -11
  51. package/lib/common/winston.js +0 -26
  52. package/lib/common/xBuild.d.ts +0 -17
  53. package/lib/common/xBuild.js +0 -80
  54. package/lib/common/xml.d.ts +0 -17
  55. package/lib/common/xml.js +0 -30
  56. package/lib/index.d.ts +0 -143
  57. package/lib/index.js +0 -8
  58. package/lib/koa.d.ts +0 -21
  59. package/lib/koa.js +0 -130
  60. package/lib/routes/calendar/calendar/calendar-multiget.d.ts +0 -7
  61. package/lib/routes/calendar/calendar/calendar-multiget.js +0 -68
  62. package/lib/routes/calendar/calendar/calendar-query.d.ts +0 -7
  63. package/lib/routes/calendar/calendar/calendar-query.js +0 -68
  64. package/lib/routes/calendar/calendar/delete.d.ts +0 -5
  65. package/lib/routes/calendar/calendar/delete.js +0 -40
  66. package/lib/routes/calendar/calendar/eventResponse.d.ts +0 -7
  67. package/lib/routes/calendar/calendar/eventResponse.js +0 -36
  68. package/lib/routes/calendar/calendar/expand-property.d.ts +0 -7
  69. package/lib/routes/calendar/calendar/expand-property.js +0 -15
  70. package/lib/routes/calendar/calendar/get.d.ts +0 -5
  71. package/lib/routes/calendar/calendar/get.js +0 -30
  72. package/lib/routes/calendar/calendar/propfind.d.ts +0 -8
  73. package/lib/routes/calendar/calendar/propfind.js +0 -76
  74. package/lib/routes/calendar/calendar/proppatch.d.ts +0 -5
  75. package/lib/routes/calendar/calendar/proppatch.js +0 -53
  76. package/lib/routes/calendar/calendar/put.d.ts +0 -5
  77. package/lib/routes/calendar/calendar/put.js +0 -68
  78. package/lib/routes/calendar/calendar/report.d.ts +0 -5
  79. package/lib/routes/calendar/calendar/report.js +0 -39
  80. package/lib/routes/calendar/calendar/sync-collection.d.ts +0 -10
  81. package/lib/routes/calendar/calendar/sync-collection.js +0 -57
  82. package/lib/routes/calendar/calendar.d.ts +0 -3
  83. package/lib/routes/calendar/calendar.js +0 -78
  84. package/lib/routes/calendar/user/propfind.d.ts +0 -5
  85. package/lib/routes/calendar/user/propfind.js +0 -64
  86. package/lib/routes/calendar/user/proppatch.d.ts +0 -5
  87. package/lib/routes/calendar/user/proppatch.js +0 -52
  88. package/lib/routes/principal/principal.d.ts +0 -3
  89. package/lib/routes/principal/principal.js +0 -32
  90. package/lib/routes/principal/propfind.d.ts +0 -3
  91. package/lib/routes/principal/propfind.js +0 -50
  92. package/lib/routes/principal/report.d.ts +0 -3
  93. package/lib/routes/principal/report.js +0 -32
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ extends: ['@commitlint/config-conventional']
3
+ };
package/.editorconfig ADDED
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 2
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
package/.gitattributes ADDED
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,24 @@
1
+ name: CI
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ build:
7
+ runs-on: ${{ matrix.os }}
8
+ strategy:
9
+ matrix:
10
+ os:
11
+ - ubuntu-latest
12
+ node_version:
13
+ - 18
14
+ name: Node ${{ matrix.node_version }} on ${{ matrix.os }}
15
+ steps:
16
+ - uses: actions/checkout@v3
17
+ - name: Setup node
18
+ uses: actions/setup-node@v3
19
+ with:
20
+ node-version: ${{ matrix.node_version }}
21
+ - name: Install dependencies
22
+ run: npm install
23
+ - name: Run tests
24
+ run: npm run test
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npx --no-install commitlint --edit $1
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npx --no-install lint-staged && npm test
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ "*.md": filenames => filenames.map(filename => `remark ${filename} -qfo`),
3
+ 'package.json': 'fixpack',
4
+ '*.js': 'xo --fix'
5
+ };
package/.prettierrc.js ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ singleQuote: true,
3
+ bracketSpacing: true,
4
+ trailingComma: 'none'
5
+ };
package/.remarkignore ADDED
@@ -0,0 +1 @@
1
+ test/snapshots/**/*.md
package/.remarkrc.js ADDED
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ plugins: ['preset-github']
3
+ };
package/.xo-config.js ADDED
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ prettier: true,
3
+ space: true,
4
+ extends: ['xo-lass'],
5
+ rules: {
6
+ 'no-warning-comments': 'off'
7
+ }
8
+ };
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Sanders DeNardi <sedenardi@gmail.com> (http://www.sandersdenardi.com/) and Forward Email LLC <support@forwardemail.net> (https://forwardemail.net)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,263 +1,49 @@
1
- # caldav-adapter
1
+ # [**caldav-adapter**](https://github.com/forwardemail/caldav-adapter)
2
2
 
3
- Middleware to handle CalDAV requests to node web server. Works with
3
+ [![build status](https://github.com/forwardemail/caldav-adapter/actions/workflows/ci.yml/badge.svg)](https://github.com/forwardemail/caldav-adapter/actions/workflows/ci.yml)
4
+ [![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
5
+ [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
6
+ [![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org)
7
+ [![license](https://img.shields.io/github/license/forwardemail/caldav-adapter.svg)](LICENSE)
4
8
 
5
- * Node v8 or higher
6
- * Koa v2 or higher
9
+ > CalDAV server for Node.js and Koa. Modernized and maintained for [Forward Email](https://forwardemail.net).
7
10
 
8
- This middleware will intercept any requests to the `caldavRoot` URL, authenticate using Basic Authentication, and exposes the following URLs and methods:
9
11
 
10
- * Principal Methods: `OPTIONS`, `PROPFIND`
11
- * Calendar Home Methods: `OPTIONS`, `PROPFIND`
12
- * Calendar Methods: `OPTIONS`, `PROPFIND`, `REPORT`, `GET`, `PUT` & `DELETE` (if calendar is not read-only)
12
+ ## Table of Contents
13
13
 
14
- The following CalDAV functionality is supported:
14
+ * [Usage](#usage)
15
+ * [Install](#install)
16
+ * [Contributors](#contributors)
17
+ * [License](#license)
15
18
 
16
- * Creating, updating, deleting one-off (non-recurring) events
17
- * Creating, updating, deleting recurring events (with limited `RRULE` support, see schema)
18
- * Read-only calendars, read-only or read/write events on each calendar
19
-
20
- This module is written in Typescript, and contains exported types for all the definitions mentioned below.
21
-
22
- ## Installation
23
-
24
- ```
25
- npm i caldav-adapter
26
- ```
27
19
 
28
20
  ## Usage
29
21
 
30
- ```js
31
- const Koa = require('koa');
32
- const app = new Koa();
33
-
34
- const caldav = require('caldav-adapter');
35
- app.use(adapter.koa({
36
- authenticate: async ({ username, password }) => {
37
- if (password === 'pass') {
38
- return {
39
- principalId: username,
40
- principalName: username.toUpperCase()
41
- };
42
- }
43
- },
44
- authRealm: config.authRealm,
45
- caldavRoot: 'caldav',
46
- proId: { company: 'TestCompany', product: 'Calendar', language: 'EN' },
47
- logEnabled: true,
48
- logLevel: 'debug',
49
- data: {
50
- getCalendar: data.getCalendar,
51
- getCalendarsForPrincipal: data.getCalendarsForPrincipal,
52
- getEventsForCalendar: data.getEventsForCalendar,
53
- getEventsByDate: data.getEventsByDate,
54
- getEvent: data.getEvent,
55
- createEvent: data.createEvent,
56
- updateEvent: data.updateEvent,
57
- deleteEvent: data.deleteEvent
58
- }
59
- }));
60
- ```
61
-
62
- ### authenticate(options)
63
-
64
- * `options`: object Parameters which might have the following properties:
65
- * `username`: string
66
- * `password`: string
67
- * returns: Promise<object> Promise which resolves to the user object, which is passed to all `data.*` functions on each request.
68
-
69
- ### authRealm
70
-
71
- * required: string Realm for Basic Authentication.
72
-
73
- ### caldavRoot
74
-
75
- * optional: string Root URL for CalDAV server (default `/`)
76
-
77
- ### proId
78
-
79
- * required: string Product Identifier passed to [ical-generator](https://github.com/sebbo2002/ical-generator#prodidstringobject-prodid)
80
-
81
- ### logEnabled
82
-
83
- * optional: boolean Enables stdout logging via Winston (default `false`)
84
-
85
- ### logLevel
86
-
87
- * optional: string Log level used by Winston (default `debug`)
88
-
89
- ### data.getCalendar(options)
90
-
91
- * `options` Parameters which might have the following properties:
92
- * `calendarId`: string
93
- * `principalId`: string
94
- * `user`: object
95
- * returns: Promise<[calendar](#calendar-model)> Promise which resolves to the requested calendar.
96
-
97
- ### data.getCalendarsForPrincipal(options)
98
-
99
- * `options` Parameters which might have the following properties:
100
- * `principalId`: string
101
- * `user`: object
102
- * returns: Promise<[calendar](#calendar-model)[]> Promise which resolves to an array of the principal's calendars.
103
-
104
- ### data.getEventsForCalendar(options)
22
+ Please refer to the Forward Email implementation at <https://github.com/forwardemail/forwardemail.net> for usage insight.
105
23
 
106
- * `options` Parameters which might have the following properties:
107
- * `calendarId`: string
108
- * `principalId`: string
109
- * `fullData`: boolean Clients will oftentimes request all events for a calendar to check the events' `etag` (lastModifiedOn) field to see whether they need updating. Since this can be an expensive operation for some fields (like description), this will be true if the client is requesting the full `calendar-data`.
110
- * `user`: object
111
- * returns: Promise<[event](#event-model)[]> Promise which resolves to an array of the calendar's events.
112
24
 
113
- ### data.getEventsByDate(options)
25
+ ## Install
114
26
 
115
- * `options` Parameters which might have the following properties:
116
- * `calendarId`: string
117
- * `principalId`: string
118
- * `start` string> ISO date string
119
- * `end` string> ISO date string
120
- * `fullData`: object
121
- * `user`: object
122
- * returns: Promise<[event](#event-model)[]> Promise which resolves to an array of the calendars' events between the given dates.
27
+ [npm][]:
123
28
 
124
- ### data.getEvent(options)
125
-
126
- * `options` Parameters which might have the following properties:
127
- * `eventId`: string
128
- * `calendarId`: string
129
- * `principalId`: string
130
- * `fullData`: object
131
- * `user`: object
132
- * returns: Promise<[event](#event-model)> Promise which resolves to an event.
133
-
134
- ### data.createEvent(options)
135
-
136
- * `options` Parameters which might have the following properties:
137
- * `event`: [event](#event-model)
138
- * `calendarId`: string
139
- * `principalId`: string
140
- * `user`: object
141
- * returns: Promise<[event](#event-model)> Promise which resolves to the created event.
142
-
143
- ### data.deleteEvent(options)
144
-
145
- * `options` Parameters which might have the following properties:
146
- * `eventId`: string
147
- * `calendarId`: string
148
- * `principalId`: string
149
- * `user`: object
150
- * returns: Promise<`void`> Promise which resolves to the deleted event.
151
-
152
- ### data.updateEvent(options)
153
-
154
- * `options` Parameters which might have the following properties:
155
- * `event`: [event](#event-model)
156
- * `calendarId`: string
157
- * `principalId`: string
158
- * `user`: object
159
- * returns: Promise<[event](#event-model)> Promise which resolves to the updated event.
160
-
161
- ## Models
162
-
163
- ### calendar model
164
-
165
- * `order`, `color` are unused for now
166
-
167
- ```json
168
- {
169
- "calendarId": "exampleCal1",
170
- "ownerId": "user@ex.co",
171
- "calendarName": "Example Calendar 1",
172
- "timeZone": "America/New_York",
173
- "order": 1,
174
- "readOnly": true,
175
- "color": "#FD8208",
176
- "syncToken": "https://example.com/ns/sync-token/1",
177
- "createdOn": "20180802T152540Z"
178
- }
29
+ ```sh
30
+ npm install caldav-adapter
179
31
  ```
180
32
 
181
- ### event model
182
33
 
183
- * dates can be anything the `moment` constructor can handle (Date or moment objects, ISO string, etc.)
184
- * `ical` field is only returned from the middleware, it's unused in responses
185
- * `recurring` only supports the `freq`, `until`, and `exdate` fields
186
- * `recurrences` is an optional array of recurrence exceptions
34
+ ## Contributors
187
35
 
188
- ```json
189
- {
190
- "eventId": "30946424-40f4-47e2-80b1-006fdebbeb25",
191
- "calendarId": "exampleCal2",
192
- "summary": "Testing Event 6",
193
- "location": "Location 6",
194
- "description": "Recurring event on Friday, 4-6pm",
195
- "startDate": "20190111T210000Z",
196
- "endDate": "20190111T230000Z",
197
- "timeZone": "America/New_York",
198
- "createdOn": "20190226T203808Z",
199
- "lastModifiedOn": "20190226T211704Z",
200
- "ical": "BEGIN:VCALENDAR...",
201
- "recurring": {
202
- "freq": "WEEKLY",
203
- "until": "20190211T170000Z",
204
- "exdate": [
205
- "20190301T210000Z",
206
- "20190315T200000Z",
207
- "20190308T210000Z"
208
- ],
209
- "recurrences": [
210
- {
211
- "recurrenceId": "20190322T200000Z",
212
- "summary": "Testing Event 6",
213
- "location": "Location 6",
214
- "description": "Recurring event on Friday, 4-6pm",
215
- "startDate": "20190322T140000Z",
216
- "endDate": "20190322T160000Z",
217
- "timeZone": "America/New_York",
218
- "createdOn": "20190226T203808Z",
219
- "lastModifiedOn": "20190226T202744Z"
220
- }
221
- ]
222
- }
223
- }
224
- ```
36
+ | Name | Website |
37
+ | ------------------- | -------------------------------- |
38
+ | **Sanders DeNardi** | <http://www.sandersdenardi.com/> |
39
+ | **Forward Email** | <https://forwardemail.net> |
225
40
 
226
- ## Examples
227
41
 
228
- Please see `exampe/server.js` for example middleware implementation, as well as `example/data.js` for `data.*` method examples.
229
-
230
- If you run the server without changing any of the configuration options, you can connect to it via iPhone or MacOS with the following server details:
231
-
232
- * Server: https://localhost:3001/caldav/p/user@ex.co
233
- * User Name: user@ex.co
234
- * Password: pass
235
-
236
- **NOTE**: You will need to install a self-signed HTTPS certificate on your local machine to connect directly to `localhost`. Otherwise you can use `ngrok` or a similar service to create a secure connection to a server on your local machine.
237
-
238
- ## Tested Clients
239
-
240
- * MacOS 10.14
241
- * Calendar.app
242
- * Mozilla Thunderbird
243
- * iOS 12
244
- * Calendar.app
245
- * Google Calendar
246
-
247
- ## Next
248
-
249
- * Android tests
250
-
251
- ## Future
252
-
253
- * Adding tests (integration tests with a dummy CalDAV client)
254
- * Add scheduling support (inbox/outbox calendars, defined in the [RFC here](https://tools.ietf.org/html/rfc6638)
255
- * Add support for Express and other frameworks
42
+ ## License
256
43
 
257
- ## Contributing
44
+ [MIT](LICENSE) © Sanders DeNardi and Forward Email LLC
258
45
 
259
- Pull requests are more than welcome! Please follow existing naming and style conventions, and correct any linting errors before submitting code.
260
46
 
261
- ## License
47
+ ##
262
48
 
263
- MIT
49
+ [npm]: https://www.npmjs.com
@@ -0,0 +1,16 @@
1
+ const ical = require('node-ical');
2
+ const raw = require('raw-body');
3
+ const { DOMParser } = require('@xmldom/xmldom');
4
+
5
+ module.exports = async function (ctx) {
6
+ ctx.request.body = await raw(ctx.req, {
7
+ encoding: true,
8
+ limit: '1mb' // Practical
9
+ });
10
+
11
+ if (ctx.request.type.includes('xml')) {
12
+ ctx.request.xml = new DOMParser().parseFromString(ctx.request.body);
13
+ } else if (ctx.request.type === 'text/calendar') {
14
+ ctx.request.ical = await ical.async.parseICS(ctx.request.body);
15
+ }
16
+ };
@@ -0,0 +1,51 @@
1
+ const setAllowHeader = function (ctx, methods = []) {
2
+ ctx.set('Allow', methods.join(', '));
3
+ };
4
+
5
+ const setDAVHeader = function (ctx) {
6
+ ctx.set(
7
+ 'DAV',
8
+ [
9
+ '1',
10
+ // '2',
11
+ '3',
12
+ // 'extended-mkcol',
13
+ 'calendar-access',
14
+ 'calendar-schedule'
15
+ // 'calendar-auto-schedule',
16
+ /* https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-proxy.txt */
17
+ // 'calendar-proxy',
18
+ // 'calendarserver-sharing',
19
+ // 'calendarserver-subscribed',
20
+ // 'access-control',
21
+ /* https://tools.ietf.org/html/rfc3744#section-9.4 */
22
+ // 'calendarserver-principal-property-search'
23
+ ].join(', ')
24
+ );
25
+ };
26
+
27
+ const setXMLHeader = function (ctx) {
28
+ ctx.set('Content-Type', 'application/xml; charset="utf-8"');
29
+ };
30
+
31
+ module.exports = {};
32
+
33
+ /* https://tools.ietf.org/html/rfc4791#section-5.1.1 */
34
+ module.exports.setOptions = function (ctx, methods = []) {
35
+ ctx.status = 200;
36
+ setAllowHeader(ctx, methods);
37
+ setDAVHeader(ctx);
38
+ ctx.body = '';
39
+ };
40
+
41
+ /* https://tools.ietf.org/html/rfc4791#section-7.8.1 */
42
+ module.exports.setMultistatusResponse = function (ctx) {
43
+ ctx.status = 207;
44
+ setDAVHeader(ctx);
45
+ setXMLHeader(ctx);
46
+ };
47
+
48
+ module.exports.setMissingMethod = function (ctx) {
49
+ ctx.status = 404;
50
+ ctx.set('Content-Type', 'text/html; charset="utf-8"');
51
+ };