Skip to content

Commit df6ec96

Browse files
committed
2 parents f7e0662 + dfbd87f commit df6ec96

28 files changed

Lines changed: 2383 additions & 376 deletions

File tree

.github/workflows/cd_dev.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ jobs:
2727
envkey_TINYPEN: ${{ secrets.TINYPENDEV }}
2828
envkey_AUDIENCE: ${{ secrets.AUDIENCE }}
2929
envkey_DOMAIN: ${{ secrets.DOMAIN }}
30+
envkey_TPEN_SUPPORT_EMAIL: ${{ secrets.TPEN_SUPPORT_EMAIL }}
31+
envkey_SMTP_HOST: ${{ secrets.SMTP_HOST }}
32+
envkey_SMTP_PORT: ${{ secrets.SMTP_PORT }}
33+
envkey_TPEN_EMAIL_CC: ${{ secrets.TPEN_EMAIL_CC }}
3034
- name: Setup Node.js
3135
uses: actions/setup-node@master
3236
with:

.github/workflows/cd_prod.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ jobs:
2727
envkey_TINYPEN: ${{ secrets.TINYPEN }}
2828
envkey_AUDIENCE: ${{ secrets.AUDIENCE }}
2929
envkey_DOMAIN: ${{ secrets.DOMAIN }}
30+
envkey_TPEN_SUPPORT_EMAIL: ${{ secrets.TPEN_SUPPORT_EMAIL }}
31+
envkey_SMTP_HOST: ${{ secrets.SMTP_HOST }}
32+
envkey_SMTP_PORT: ${{ secrets.SMTP_PORT }}
33+
envkey_TPEN_EMAIL_CC: ${{ secrets.TPEN_EMAIL_CC }}
3034
- name: Setup Node.js
3135
uses: actions/setup-node@master
3236
with:

.github/workflows/ci_dev.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ jobs:
2727
envkey_TINYPEN: ${{ secrets.TINYPENDEV }}
2828
envkey_AUDIENCE: ${{ secrets.AUDIENCE }}
2929
envkey_DOMAIN: ${{ secrets.DOMAIN }}
30+
envkey_TPEN_SUPPORT_EMAIL: ${{ secrets.TPEN_SUPPORT_EMAIL }}
31+
envkey_SMTP_HOST: ${{ secrets.SMTP_HOST }}
32+
envkey_SMTP_PORT: ${{ secrets.SMTP_PORT }}
33+
envkey_TPEN_EMAIL_CC: ${{ secrets.TPEN_EMAIL_CC }}
3034
- name: Setup Node.js
3135
uses: actions/setup-node@master
3236
with:
@@ -47,4 +51,4 @@ jobs:
4751
run: |
4852
npm install
4953
npm run E2Etests
50-
54+

.github/workflows/ci_prod.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ jobs:
2727
envkey_TINYPEN: ${{ secrets.TINYPEN }}
2828
envkey_AUDIENCE: ${{ secrets.AUDIENCE }}
2929
envkey_DOMAIN: ${{ secrets.DOMAIN }}
30+
envkey_TPEN_SUPPORT_EMAIL: ${{ secrets.TPEN_SUPPORT_EMAIL }}
31+
envkey_SMTP_HOST: ${{ secrets.SMTP_HOST }}
32+
envkey_SMTP_PORT: ${{ secrets.SMTP_PORT }}
33+
envkey_TPEN_EMAIL_CC: ${{ secrets.TPEN_EMAIL_CC }}
3034
- name: Setup Node.js
3135
uses: actions/setup-node@master
3236
with:
@@ -47,4 +51,4 @@ jobs:
4751
run: |
4852
npm install
4953
npm run E2Etests
50-
54+

API.md

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# TPEN Services API Documentation
2+
3+
This document provides an overview of the available routes in the TPEN Services API. These routes allow interaction with the application for various functionalities.
4+
5+
## Base URL
6+
7+
- **Production**: [https://api.t-pen.org](https://api.t-pen.org)
8+
- **Development**: [https://dev.api.t-pen.org](https://dev.api.t-pen.org)
9+
10+
## Endpoints
11+
12+
### 1. **Authentication**
13+
14+
Endpoints marked with a 🔐 requiring authentication expect a valid JWT token in the `Authorization` header. Use the [public TPEN3 login](https://three.t-pen.org/login/) to get one of these JWT tokens.
15+
16+
---
17+
18+
### 2. **Project**
19+
20+
#### `POST /project/create` 🔐
21+
22+
- **Description**: Create a new project. This is a high-skill maneuver requiring a complete project object.
23+
- **Request Body**:
24+
25+
```json
26+
{
27+
"label": "string",
28+
"metadata": [ { Metadata } ],
29+
"layers": [ { Layer } ],
30+
"manifests": [ "URIs<Manifest>" ],
31+
"creator": "URI<Agent>",
32+
"group": "hexstring"
33+
}
34+
```
35+
36+
- **Responses**:
37+
38+
- **201**: Project created successfully
39+
- **400**: Project creation failed, validation errors
40+
- **401**: Unauthorized
41+
- **500**: Server error
42+
43+
The Location header of a successful response is the Project id.
44+
45+
#### `POST /import?createFrom="URL"` 🔐
46+
47+
- **Description**: Create a new project by importing a web resource.
48+
- **Request Body**:
49+
50+
```json
51+
{
52+
"url": "URL<IIIF:Manifest>"
53+
}
54+
```
55+
56+
- **Responses**:
57+
58+
- **201**: Project created successfully
59+
- **400**: Project creation failed, validation errors
60+
- **401**: Unauthorized
61+
- **500**: Server error
62+
63+
The Location header of a successful response is the Project id. The project will be created with the label and metadata of the imported resource. A complete project object will be created with the imported resource as the first manifest and returned.
64+
65+
#### `GET /project/:id` 🔐
66+
67+
- **Description**: Retrieve a project by ID.
68+
- **Parameters**:
69+
- `id`: ID of the project.
70+
- **Responses**:
71+
72+
- **200**: Project found
73+
```json
74+
{
75+
"id": "string",
76+
"label": "string",
77+
"metadata": [ { Metadata } ],
78+
"creator": "URI<Agent>",
79+
"layers": [ { Layer } ],
80+
"manifests": [ "URIs<Manifest>" ],
81+
"group": "hexstring",
82+
"tools": [ "string" ],
83+
"options": { OptionsMap }
84+
}
85+
```
86+
- **404**: Project not found
87+
- **401**: Unauthorized
88+
- **403**: Forbidden
89+
- **500**: Server error
90+
91+
The response is not a complete Project data object, but a projection designed for use in the client interface.
92+
93+
---
94+
95+
### 3. **Collaborators**
96+
97+
Manage the users and roles in a Project.
98+
99+
#### `POST /project/:projectId/invite-member` 🔐
100+
101+
- **Description**: Invite a user to a project. If the user does not have a TPEN account, they will be sent an email invitation.
102+
- **Parameters**:
103+
- `projectId`: ID of the project as a hexstring or the Project slug.
104+
- **Request Body**:
105+
106+
```json
107+
{
108+
"email": "string",
109+
"roles": ["string"] | "string"
110+
}
111+
```
112+
113+
- `email`: The email address of the user to invite.
114+
- `roles`: The roles of the user in the project as an array or space-delimited string.
115+
- **Responses**:
116+
117+
- **200**: User invited successfully
118+
- **400**: User invitation failed, validation errors
119+
- **401**: Unauthorized
120+
- **403**: Forbidden
121+
- **500**: Server error
122+
123+
This API is not able to track the status of the invitation. Email addresses that fail to be delivered or are rejected by the recipient will not be reported.
124+
125+
#### `POST /project/:projectId/remove-member` 🔐
126+
127+
- **Description**: Remove a user from a project group.
128+
- **Parameters**:
129+
- `projectId`: ID of the project.
130+
- **Request Body**:
131+
132+
```json
133+
{
134+
"userID": "string"
135+
}
136+
```
137+
- **Responses**:
138+
139+
- **204**: User removed successfully
140+
- **401**: Unauthorized
141+
- **403**: Forbidden
142+
- **500**: Server error
143+
144+
This removes a user and their roles from the project. The user will no longer have access to the project, but their annotations will remain with full attribution.
145+
146+
#### `PUT /project/:projectId/collaborator/:collaboratorId/setRoles` 🔐
147+
148+
- **Description**: Set the roles of a User in a project, replacing all currently defined roles.
149+
- **Parameters**:
150+
- `projectId`: ID of the project.
151+
- `collaboratorId`: ID of the collaborator.
152+
- **Request Body**:
153+
154+
```json
155+
{
156+
"roles": ["string"] | "string"
157+
}
158+
```
159+
- **Responses**:
160+
161+
- **200**: Roles set successfully
162+
- **400**: Roles setting failed, validation errors
163+
- **401**: Unauthorized
164+
- **403**: Forbidden
165+
- **500**: Server error
166+
167+
The User requesting this action must have permissions to set roles for the collaborator. The <kbd>OWNER</kbd> role cannot be set using this endpoint.
168+
169+
#### `POST /project/:projectId/switch/owner` 🔐
170+
171+
- **Description**: Transfer ownership of a project to another user.
172+
- **Parameters**:
173+
- `projectId`: ID of the project.
174+
- **Request Body**:
175+
176+
```json
177+
{
178+
"newOwnerId": "hexstring"
179+
}
180+
```
181+
- **Responses**:
182+
183+
- **200**: Ownership transferred successfully
184+
- **400**: Ownership transfer failed, validation errors
185+
- **401**: Unauthorized
186+
- **403**: Forbidden
187+
- **500**: Server error
188+
189+
The User requesting this action must be the current owner of the project. The <kbd>OWNER</kbd> role will be removed from the current owner and assigned to the new owner. The new owner must be a member of the project. If the current owner has no other roles, the <kbd>CONTRIBUTOR</kbd> role will be assigned to them.
190+
191+
#### `POST /project/:projectId/setCustomRoles` 🔐
192+
193+
- **Description**: Set custom roles for a Project group. Custom roles must be provided as a JSON object with keys as roles and values as arrays of permissions or space-delimited strings.
194+
- **Parameters**:
195+
- `projectId`: ID of the project.
196+
- **Request Body**:
197+
198+
```json
199+
{
200+
"roles": {
201+
"roleName": ["permission1", "permission2"] | "space-delimited permissions"
202+
}
203+
}
204+
```
205+
206+
- **Responses**:
207+
208+
- **200**: Custom roles set successfully
209+
- **400**: Invalid request, validation errors
210+
- **401**: Unauthenticated request
211+
- **403**: Permission denied
212+
- **500**: Server error
213+
214+
The User requesting this action must have permissions to update roles. Default roles cannot be modified using this endpoint. Custom roles can be complicated and there is more detailed description of their use in the [Cookbook](#).
215+
216+
---
217+
218+
### 4. **Users**
219+
220+
Private account modification, personal details, and public profile retrieval.
221+
222+
#### `GET /my/profile` 🔐
223+
224+
- **Description**: Retrieve the profile of the authenticated user.
225+
- **Responses**:
226+
227+
- **200**: Profile found
228+
```json
229+
{
230+
"_id": "hexstring",
231+
"_sub": "string",
232+
"agent": "URI<Agent>",
233+
"email": "string",
234+
"profile": {
235+
"displayName": "string",
236+
"custom": Any
237+
}
238+
"name": "string"
239+
}
240+
```
241+
- **401**: Unauthorized
242+
- **500**: Server error
243+
244+
Users can always see and manipulate their own profile. The profile object is a projection of the full user object.
245+
246+
#### `GET /my/projects` 🔐
247+
248+
- **Description**: Retrieve a list of projects the authenticated user is a member of.
249+
- **Responses**:
250+
251+
- **200**: Projects found
252+
```json
253+
[
254+
{
255+
"_id": "hexstring",
256+
"label": "string",
257+
"roles": ["string"]
258+
}, ...
259+
]
260+
```
261+
- **401**: Unauthorized
262+
- **500**: Server error
263+
264+
The response is a list of projects the user is a member of regardless of the permissions afforded to them in each project.
265+
266+
#### `GET /user/:id`
267+
268+
- **Description**: Retrieve the public profile of a user.
269+
- **Parameters**:
270+
- `id`: ID of the user.
271+
- **Responses**:
272+
273+
- **200**: Profile found
274+
```json
275+
{
276+
"_id": "hexstring",
277+
"displayName": "string",
278+
"custom": Any
279+
}
280+
```
281+
- **404**: Profile not found
282+
- **500**: Server error
283+
284+
The response is a projection of the full user object. The public profile is available to all users and serialized into this response with whatever the user has chosen to share. The `custom` field is not an actual property - it is a placeholder for any additional fields the user has added to their profile. The `displayName` and `_id` fields are always present.

app.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ import manifestRouter from './manifest/index.mjs'
2424
import projectRouter from './project/index.mjs'
2525
import pageRouter from './page/index.mjs'
2626
import lineRouter from './line/index.mjs'
27-
import userProfileRouter from './userProfile/index.mjs'
27+
import userProfileRouter from './userProfile/index.mjs'
2828
import privateProfileRouter from './userProfile/privateProfile.mjs'
29+
import proxyRouter from './utilities/proxy.js'
2930

3031
let app = express()
3132

@@ -62,6 +63,7 @@ app.use('/line', lineRouter)
6263
app.use('/page', pageRouter)
6364
app.use('/user', userProfileRouter)
6465
app.use('/my', privateProfileRouter)
66+
app.use('/proxy', proxyRouter)
6567

6668
//catch 404 because of an invalid site path
6769
app.use('*', function(req, res, next) {

classes/Group/Group.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export default class Group {
99
}
1010

1111
async #loadFromDB() {
12-
this.data = await database.getById(this._id, "groups")
12+
this.data = await database.getById(this._id, process.env.TPENGROUPS)
1313
return this
1414
}
1515

@@ -228,13 +228,13 @@ export default class Group {
228228
}
229229

230230
async save() {
231-
this.validateGroup()
231+
await this.validateGroup()
232232
return database.save(this.data, process.env.TPENGROUPS)
233233
}
234234

235235
async update() {
236236
await this.validateGroup()
237-
return database.update({ ...this.data, type: "Group" },)
237+
return database.update(this.data, process.env.TPENGROUPS)
238238
}
239239

240240
async validateGroup() {

0 commit comments

Comments
 (0)