javascript-solid-server 0.0.16 → 0.0.18
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.
- package/.claude/settings.local.json +8 -1
- package/LICENSE +661 -0
- package/README.md +28 -2
- package/bin/jss.js +6 -0
- package/data/.idp/accounts/_email_index.json +3 -0
- package/data/.idp/accounts/_webid_index.json +3 -0
- package/data/.idp/accounts/be4cfcd3-f6dd-41de-b2f5-7e4045d3e78c.json +9 -0
- package/data/.idp/authorization_code/6MiwPzYssbmBMdZ0_yp9RUmeGKJpmChhcPNAT-xkwE1.json +18 -0
- package/data/.idp/authorization_code/eC1ZBx2mpNsr0OjKMKdkKK69E4EfKV1SwU8p0dWFQTj.json +18 -0
- package/data/.idp/authorization_code/puWy-HQtx9ONFDhm_Bv-sRcBfm3OQPppRalQ956hLR4.json +18 -0
- package/data/.idp/client/client_mjocmpsj_vdrkczw9.json +25 -0
- package/data/.idp/client/client_mjocnc8k_3sd1aoa6.json +25 -0
- package/data/.idp/client/client_mjocnlbf_dkm0ltze.json +25 -0
- package/data/.idp/grant/ImpM7BIsyKuhvme7UOctBmRUicTsZweHOrHk95wza0s.json +16 -0
- package/data/.idp/grant/R2aOui_2A-m6E_aeq_03IyMd6N5OrFJ-lT67cCTuzOL.json +16 -0
- package/data/.idp/grant/YFxdDUi4neEPXjx_riL4_Tyg_VXuyhax_qm9yFEvrWG.json +16 -0
- package/data/.idp/grant/k5rDpXSPbMMzYLaWILONhIojmzNP6f1hkWxajC_weW3.json +16 -0
- package/data/.idp/grant/luO7y4I33a7yWi1BnfgOtoNcr1-5vH8l3t-aIqG8IgI.json +16 -0
- package/data/.idp/grant/nkXTstzTTyrUEN5H0f8Q-YR5jMqk0WdDc6H6H1XD6lJ.json +16 -0
- package/data/.idp/registration_access_token/CPhAs33MGp8s-gCRvdnbqjdDDdi0g9vNwacfuLGhX6L.json +7 -0
- package/data/.idp/registration_access_token/dbOnxWLEW5O2_pYEfCFQJYbvpdqJJ_t35gr1dd7jC_6.json +7 -0
- package/data/.idp/registration_access_token/lAFc6diCNs1g9KLYHRD75O-cH5uv4dRX09HPIelMZbE.json +7 -0
- package/data/.idp/session/VXgvz6cHkuwDGG3Hp7CYWzck4AlTDH9qCOnCxVVAGOb.json +28 -0
- package/data/demo/.acl +47 -0
- package/data/demo/inbox/.acl +50 -0
- package/data/demo/index.html +80 -0
- package/data/demo/private/.acl +32 -0
- package/data/demo/public/.acl +50 -0
- package/data/demo/public/card.ttl +8 -0
- package/data/demo/settings/.acl +32 -0
- package/data/demo/settings/prefs +17 -0
- package/data/demo/settings/privateTypeIndex +7 -0
- package/data/demo/settings/publicTypeIndex +7 -0
- package/package.json +2 -2
- package/src/config.js +7 -0
- package/src/handlers/resource.js +22 -3
- package/src/idp/interactions.js +13 -12
- package/src/mashlib/index.js +98 -0
- package/src/server.js +7 -0
- package/src/utils/url.js +18 -2
- package/test-data-idp-accounts/.idp/accounts/_email_index.json +1 -1
- package/test-data-idp-accounts/.idp/accounts/_webid_index.json +1 -1
- package/test-data-idp-accounts/.idp/accounts/b49949d9-6d61-45a1-bcee-07295aa07579.json +9 -0
- package/test-data-idp-accounts/.idp/keys/jwks.json +8 -8
- package/test-data-idp-accounts/.idp/accounts/3c1cd503-1d7f-4ba0-a3af-ebedf519594d.json +0 -9
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": {
|
|
3
|
+
"acl": "http://www.w3.org/ns/auth/acl#",
|
|
4
|
+
"foaf": "http://xmlns.com/foaf/0.1/"
|
|
5
|
+
},
|
|
6
|
+
"@graph": [
|
|
7
|
+
{
|
|
8
|
+
"@id": "#owner",
|
|
9
|
+
"@type": "acl:Authorization",
|
|
10
|
+
"acl:agent": {
|
|
11
|
+
"@id": "http://localhost:4000/demo/#me"
|
|
12
|
+
},
|
|
13
|
+
"acl:accessTo": {
|
|
14
|
+
"@id": "http://localhost:4000/demo/public/"
|
|
15
|
+
},
|
|
16
|
+
"acl:default": {
|
|
17
|
+
"@id": "http://localhost:4000/demo/public/"
|
|
18
|
+
},
|
|
19
|
+
"acl:mode": [
|
|
20
|
+
{
|
|
21
|
+
"@id": "acl:Read"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"@id": "acl:Write"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"@id": "acl:Control"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"@id": "#public",
|
|
33
|
+
"@type": "acl:Authorization",
|
|
34
|
+
"acl:agentClass": {
|
|
35
|
+
"@id": "foaf:Agent"
|
|
36
|
+
},
|
|
37
|
+
"acl:accessTo": {
|
|
38
|
+
"@id": "http://localhost:4000/demo/public/"
|
|
39
|
+
},
|
|
40
|
+
"acl:default": {
|
|
41
|
+
"@id": "http://localhost:4000/demo/public/"
|
|
42
|
+
},
|
|
43
|
+
"acl:mode": [
|
|
44
|
+
{
|
|
45
|
+
"@id": "acl:Read"
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
2
|
+
@prefix solid: <http://www.w3.org/ns/solid/terms#> .
|
|
3
|
+
@prefix schema: <http://schema.org/> .
|
|
4
|
+
|
|
5
|
+
<#me> a foaf:Person ;
|
|
6
|
+
foaf:name "Demo User" ;
|
|
7
|
+
foaf:mbox <mailto:demo@example.com> ;
|
|
8
|
+
schema:knows <https://melvincarvalho.com/#me> .
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": {
|
|
3
|
+
"acl": "http://www.w3.org/ns/auth/acl#",
|
|
4
|
+
"foaf": "http://xmlns.com/foaf/0.1/"
|
|
5
|
+
},
|
|
6
|
+
"@graph": [
|
|
7
|
+
{
|
|
8
|
+
"@id": "#owner",
|
|
9
|
+
"@type": "acl:Authorization",
|
|
10
|
+
"acl:agent": {
|
|
11
|
+
"@id": "http://localhost:4000/demo/#me"
|
|
12
|
+
},
|
|
13
|
+
"acl:accessTo": {
|
|
14
|
+
"@id": "http://localhost:4000/demo/settings/"
|
|
15
|
+
},
|
|
16
|
+
"acl:mode": [
|
|
17
|
+
{
|
|
18
|
+
"@id": "acl:Read"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"@id": "acl:Write"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"@id": "acl:Control"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"acl:default": {
|
|
28
|
+
"@id": "http://localhost:4000/demo/settings/"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": {
|
|
3
|
+
"solid": "http://www.w3.org/ns/solid/terms#",
|
|
4
|
+
"pim": "http://www.w3.org/ns/pim/space#",
|
|
5
|
+
"publicTypeIndex": {
|
|
6
|
+
"@id": "solid:publicTypeIndex",
|
|
7
|
+
"@type": "@id"
|
|
8
|
+
},
|
|
9
|
+
"privateTypeIndex": {
|
|
10
|
+
"@id": "solid:privateTypeIndex",
|
|
11
|
+
"@type": "@id"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"@id": "http://localhost:4000/demo/settings/prefs",
|
|
15
|
+
"publicTypeIndex": "http://localhost:4000/demo/settings/publicTypeIndex",
|
|
16
|
+
"privateTypeIndex": "http://localhost:4000/demo/settings/privateTypeIndex"
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "javascript-solid-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "A minimal, fast Solid server",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"linked-data",
|
|
43
43
|
"decentralized"
|
|
44
44
|
],
|
|
45
|
-
"license": "
|
|
45
|
+
"license": "AGPL-3.0-only",
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"autocannon": "^8.0.0"
|
|
48
48
|
}
|
package/src/config.js
CHANGED
|
@@ -37,6 +37,10 @@ export const defaults = {
|
|
|
37
37
|
subdomains: false,
|
|
38
38
|
baseDomain: null,
|
|
39
39
|
|
|
40
|
+
// Mashlib data browser
|
|
41
|
+
mashlib: false,
|
|
42
|
+
mashlibVersion: '2.0.0',
|
|
43
|
+
|
|
40
44
|
// Logging
|
|
41
45
|
logger: true,
|
|
42
46
|
quiet: false,
|
|
@@ -63,6 +67,8 @@ const envMap = {
|
|
|
63
67
|
JSS_IDP_ISSUER: 'idpIssuer',
|
|
64
68
|
JSS_SUBDOMAINS: 'subdomains',
|
|
65
69
|
JSS_BASE_DOMAIN: 'baseDomain',
|
|
70
|
+
JSS_MASHLIB: 'mashlib',
|
|
71
|
+
JSS_MASHLIB_VERSION: 'mashlibVersion',
|
|
66
72
|
};
|
|
67
73
|
|
|
68
74
|
/**
|
|
@@ -195,5 +201,6 @@ export function printConfig(config) {
|
|
|
195
201
|
console.log(` Notifications: ${config.notifications}`);
|
|
196
202
|
console.log(` IdP: ${config.idp ? (config.idpIssuer || 'enabled') : 'disabled'}`);
|
|
197
203
|
console.log(` Subdomains: ${config.subdomains ? (config.baseDomain || 'enabled') : 'disabled'}`);
|
|
204
|
+
console.log(` Mashlib: ${config.mashlib ? `v${config.mashlibVersion}` : 'disabled'}`);
|
|
198
205
|
console.log('─'.repeat(40));
|
|
199
206
|
}
|
package/src/handlers/resource.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '../rdf/conneg.js';
|
|
15
15
|
import { emitChange } from '../notifications/events.js';
|
|
16
16
|
import { checkIfMatch, checkIfNoneMatchForGet, checkIfNoneMatchForWrite } from '../utils/conditional.js';
|
|
17
|
+
import { generateDatabrowserHtml, shouldServeMashlib } from '../mashlib/index.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Get the storage path and resource URL for a request
|
|
@@ -134,14 +135,32 @@ export async function handleGet(request, reply) {
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
// Handle resource
|
|
138
|
+
const storedContentType = getContentType(storagePath);
|
|
139
|
+
const connegEnabled = request.connegEnabled || false;
|
|
140
|
+
|
|
141
|
+
// Check if we should serve Mashlib data browser
|
|
142
|
+
// Only for RDF resources when Accept: text/html is requested
|
|
143
|
+
if (shouldServeMashlib(request, request.mashlibEnabled, storedContentType)) {
|
|
144
|
+
const html = generateDatabrowserHtml(resourceUrl, request.mashlibVersion);
|
|
145
|
+
const headers = getAllHeaders({
|
|
146
|
+
isContainer: false,
|
|
147
|
+
etag: stats.etag,
|
|
148
|
+
contentType: 'text/html',
|
|
149
|
+
origin,
|
|
150
|
+
resourceUrl,
|
|
151
|
+
connegEnabled
|
|
152
|
+
});
|
|
153
|
+
headers['Vary'] = 'Accept';
|
|
154
|
+
|
|
155
|
+
Object.entries(headers).forEach(([k, v]) => reply.header(k, v));
|
|
156
|
+
return reply.type('text/html').send(html);
|
|
157
|
+
}
|
|
158
|
+
|
|
137
159
|
const content = await storage.read(storagePath);
|
|
138
160
|
if (content === null) {
|
|
139
161
|
return reply.code(500).send({ error: 'Read error' });
|
|
140
162
|
}
|
|
141
163
|
|
|
142
|
-
const storedContentType = getContentType(storagePath);
|
|
143
|
-
const connegEnabled = request.connegEnabled || false;
|
|
144
|
-
|
|
145
164
|
// Content negotiation for RDF resources
|
|
146
165
|
if (connegEnabled && isRdfContentType(storedContentType)) {
|
|
147
166
|
try {
|
package/src/idp/interactions.js
CHANGED
|
@@ -118,20 +118,22 @@ export async function handleLogin(request, reply, provider) {
|
|
|
118
118
|
|
|
119
119
|
request.log.info({ accountId: account.id, uid }, 'Login successful');
|
|
120
120
|
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
//
|
|
126
|
-
// We use interactionResult to get the redirect URL, then save it and return JSON
|
|
127
|
-
|
|
128
|
-
// Save the login result to the interaction for programmatic clients
|
|
129
|
-
// This allows the auth endpoint to continue the flow when resumed
|
|
121
|
+
// Detect if this is a browser (wants HTML/redirect) or programmatic client (wants JSON)
|
|
122
|
+
const acceptHeader = request.headers.accept || '';
|
|
123
|
+
const wantsBrowserRedirect = acceptHeader.includes('text/html') && !acceptHeader.includes('application/json');
|
|
124
|
+
|
|
125
|
+
// Save the login result to the interaction
|
|
130
126
|
interaction.result = result;
|
|
131
127
|
await interaction.save(interaction.exp - Math.floor(Date.now() / 1000));
|
|
132
128
|
|
|
133
|
-
// For
|
|
134
|
-
|
|
129
|
+
// For browsers (mashlib, etc): do a proper HTTP redirect
|
|
130
|
+
if (wantsBrowserRedirect) {
|
|
131
|
+
reply.hijack();
|
|
132
|
+
return provider.interactionFinished(request.raw, reply.raw, result, { mergeWithLastSubmission: false });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// For CTH and programmatic clients: return JSON with location
|
|
136
|
+
// CTH expects a 200 response with "location" in body (CSS v3+ style)
|
|
135
137
|
try {
|
|
136
138
|
reply.hijack();
|
|
137
139
|
|
|
@@ -188,7 +190,6 @@ export async function handleLogin(request, reply, provider) {
|
|
|
188
190
|
request.log.warn({ err: err.message, errName: err.name, uid }, 'interactionFinished failed, using fallback');
|
|
189
191
|
|
|
190
192
|
// Fallback: return the redirect URL for manual following
|
|
191
|
-
// The interaction result is already saved above
|
|
192
193
|
const redirectTo = `/idp/auth/${uid}`;
|
|
193
194
|
return reply
|
|
194
195
|
.code(200)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mashlib Data Browser Integration
|
|
3
|
+
*
|
|
4
|
+
* Generates HTML wrapper that loads SolidOS Mashlib from CDN.
|
|
5
|
+
* When a browser requests an RDF resource with Accept: text/html,
|
|
6
|
+
* we return this wrapper which then fetches and renders the data.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const CDN_BASE = 'https://unpkg.com/mashlib';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generate Mashlib databrowser HTML
|
|
13
|
+
* @param {string} resourceUrl - The URL of the resource being viewed
|
|
14
|
+
* @param {string} version - Mashlib version (default: '2.0.0')
|
|
15
|
+
* @returns {string} HTML content
|
|
16
|
+
*/
|
|
17
|
+
export function generateDatabrowserHtml(resourceUrl, version = '2.0.0') {
|
|
18
|
+
const cdnUrl = `${CDN_BASE}@${version}/dist`;
|
|
19
|
+
|
|
20
|
+
return `<!doctype html>
|
|
21
|
+
<html>
|
|
22
|
+
<head>
|
|
23
|
+
<meta charset="utf-8"/>
|
|
24
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
25
|
+
<title>SolidOS - ${escapeHtml(resourceUrl)}</title>
|
|
26
|
+
<script defer src="${cdnUrl}/mashlib.min.js"></script>
|
|
27
|
+
<link href="${cdnUrl}/mash.css" rel="stylesheet">
|
|
28
|
+
<script>
|
|
29
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
30
|
+
// runDataBrowser uses window.location to determine what to fetch
|
|
31
|
+
panes.runDataBrowser();
|
|
32
|
+
});
|
|
33
|
+
</script>
|
|
34
|
+
<style>
|
|
35
|
+
/* Loading indicator */
|
|
36
|
+
body:not(.loaded) #PageBody::before {
|
|
37
|
+
content: 'Loading SolidOS...';
|
|
38
|
+
display: block;
|
|
39
|
+
padding: 2em;
|
|
40
|
+
text-align: center;
|
|
41
|
+
color: #666;
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
44
|
+
</head>
|
|
45
|
+
<body id="PageBody">
|
|
46
|
+
<header id="PageHeader"></header>
|
|
47
|
+
<div class="TabulatorOutline" id="DummyUUID" role="main">
|
|
48
|
+
<table id="outline"></table>
|
|
49
|
+
<div id="GlobalDashboard"></div>
|
|
50
|
+
</div>
|
|
51
|
+
<footer id="PageFooter"></footer>
|
|
52
|
+
</body>
|
|
53
|
+
</html>`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if request wants HTML and mashlib should handle it
|
|
58
|
+
* @param {object} request - Fastify request
|
|
59
|
+
* @param {boolean} mashlibEnabled - Whether mashlib is enabled
|
|
60
|
+
* @param {string} contentType - Content type of the resource
|
|
61
|
+
* @returns {boolean}
|
|
62
|
+
*/
|
|
63
|
+
export function shouldServeMashlib(request, mashlibEnabled, contentType) {
|
|
64
|
+
if (!mashlibEnabled) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const accept = request.headers.accept || '';
|
|
69
|
+
|
|
70
|
+
// Must explicitly accept HTML
|
|
71
|
+
if (!accept.includes('text/html')) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Only serve mashlib for RDF content types
|
|
76
|
+
const rdfTypes = [
|
|
77
|
+
'text/turtle',
|
|
78
|
+
'application/ld+json',
|
|
79
|
+
'application/json',
|
|
80
|
+
'text/n3',
|
|
81
|
+
'application/n-triples',
|
|
82
|
+
'application/rdf+xml'
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
const baseType = contentType.split(';')[0].trim().toLowerCase();
|
|
86
|
+
return rdfTypes.includes(baseType);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Escape HTML special characters
|
|
91
|
+
*/
|
|
92
|
+
function escapeHtml(str) {
|
|
93
|
+
return str
|
|
94
|
+
.replace(/&/g, '&')
|
|
95
|
+
.replace(/</g, '<')
|
|
96
|
+
.replace(/>/g, '>')
|
|
97
|
+
.replace(/"/g, '"');
|
|
98
|
+
}
|
package/src/server.js
CHANGED
|
@@ -30,6 +30,9 @@ export function createServer(options = {}) {
|
|
|
30
30
|
// Subdomain mode is OFF by default - use path-based pods
|
|
31
31
|
const subdomainsEnabled = options.subdomains ?? false;
|
|
32
32
|
const baseDomain = options.baseDomain || null;
|
|
33
|
+
// Mashlib data browser is OFF by default
|
|
34
|
+
const mashlibEnabled = options.mashlib ?? false;
|
|
35
|
+
const mashlibVersion = options.mashlibVersion ?? '2.0.0';
|
|
33
36
|
|
|
34
37
|
// Set data root via environment variable if provided
|
|
35
38
|
if (options.root) {
|
|
@@ -66,12 +69,16 @@ export function createServer(options = {}) {
|
|
|
66
69
|
fastify.decorateRequest('subdomainsEnabled', null);
|
|
67
70
|
fastify.decorateRequest('baseDomain', null);
|
|
68
71
|
fastify.decorateRequest('podName', null);
|
|
72
|
+
fastify.decorateRequest('mashlibEnabled', null);
|
|
73
|
+
fastify.decorateRequest('mashlibVersion', null);
|
|
69
74
|
fastify.addHook('onRequest', async (request) => {
|
|
70
75
|
request.connegEnabled = connegEnabled;
|
|
71
76
|
request.notificationsEnabled = notificationsEnabled;
|
|
72
77
|
request.idpEnabled = idpEnabled;
|
|
73
78
|
request.subdomainsEnabled = subdomainsEnabled;
|
|
74
79
|
request.baseDomain = baseDomain;
|
|
80
|
+
request.mashlibEnabled = mashlibEnabled;
|
|
81
|
+
request.mashlibVersion = mashlibVersion;
|
|
75
82
|
|
|
76
83
|
// Extract pod name from subdomain if enabled
|
|
77
84
|
if (subdomainsEnabled && baseDomain) {
|
package/src/utils/url.js
CHANGED
|
@@ -120,7 +120,13 @@ export function getContentType(filePath) {
|
|
|
120
120
|
'.jpeg': 'image/jpeg',
|
|
121
121
|
'.gif': 'image/gif',
|
|
122
122
|
'.svg': 'image/svg+xml',
|
|
123
|
-
'.pdf': 'application/pdf'
|
|
123
|
+
'.pdf': 'application/pdf',
|
|
124
|
+
'.ttl': 'text/turtle',
|
|
125
|
+
'.n3': 'text/n3',
|
|
126
|
+
'.nt': 'application/n-triples',
|
|
127
|
+
'.rdf': 'application/rdf+xml',
|
|
128
|
+
'.nq': 'application/n-quads',
|
|
129
|
+
'.trig': 'application/trig'
|
|
124
130
|
};
|
|
125
131
|
return types[ext] || 'application/octet-stream';
|
|
126
132
|
}
|
|
@@ -131,5 +137,15 @@ export function getContentType(filePath) {
|
|
|
131
137
|
* @returns {boolean}
|
|
132
138
|
*/
|
|
133
139
|
export function isRdfContentType(contentType) {
|
|
134
|
-
|
|
140
|
+
const rdfTypes = [
|
|
141
|
+
'application/ld+json',
|
|
142
|
+
'application/json',
|
|
143
|
+
'text/turtle',
|
|
144
|
+
'text/n3',
|
|
145
|
+
'application/n-triples',
|
|
146
|
+
'application/rdf+xml',
|
|
147
|
+
'application/n-quads',
|
|
148
|
+
'application/trig'
|
|
149
|
+
];
|
|
150
|
+
return rdfTypes.includes(contentType);
|
|
135
151
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "b49949d9-6d61-45a1-bcee-07295aa07579",
|
|
3
|
+
"email": "credtest@example.com",
|
|
4
|
+
"passwordHash": "$2b$10$mVzAvASfYaz/wtb7ENo.D..AKd5CWHnOqAeL3RRPGfH20AbZG.ZEm",
|
|
5
|
+
"webId": "http://localhost:3101/credtest/#me",
|
|
6
|
+
"podName": "credtest",
|
|
7
|
+
"createdAt": "2025-12-27T13:40:23.165Z",
|
|
8
|
+
"lastLogin": "2025-12-27T13:40:23.500Z"
|
|
9
|
+
}
|
|
@@ -3,20 +3,20 @@
|
|
|
3
3
|
"keys": [
|
|
4
4
|
{
|
|
5
5
|
"kty": "EC",
|
|
6
|
-
"x": "
|
|
7
|
-
"y": "
|
|
6
|
+
"x": "LAyVHoAoNTkPv1-7GonFPGYWWh2Oo8W1bxWFGdX8fW8",
|
|
7
|
+
"y": "bntHv0EpOcvKrzlGujXkBID_7iHmp9wFte4heIrzf3Y",
|
|
8
8
|
"crv": "P-256",
|
|
9
|
-
"d": "
|
|
10
|
-
"kid": "
|
|
9
|
+
"d": "V6umt-paD0-Uk9SA-0NYZHZSOz0h9OZppYwopeZXedo",
|
|
10
|
+
"kid": "1c8e0740-f688-4a68-8231-8d2388dcd810",
|
|
11
11
|
"use": "sig",
|
|
12
12
|
"alg": "ES256",
|
|
13
|
-
"iat":
|
|
13
|
+
"iat": 1766842823
|
|
14
14
|
}
|
|
15
15
|
]
|
|
16
16
|
},
|
|
17
17
|
"cookieKeys": [
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"pq7a_Nu9u72ZeHdklGCnSocY9z4nyAkk0ZuUBU8YH7U",
|
|
19
|
+
"RzXV-DugE1lvl331HZ5Fo04A5UY2GLJn4MjAqcAZ8Ts"
|
|
20
20
|
],
|
|
21
|
-
"createdAt": "2025-12-
|
|
21
|
+
"createdAt": "2025-12-27T13:40:23.081Z"
|
|
22
22
|
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "3c1cd503-1d7f-4ba0-a3af-ebedf519594d",
|
|
3
|
-
"email": "credtest@example.com",
|
|
4
|
-
"passwordHash": "$2b$10$h3cRwsgAo/4wEOyip6ckyOf6J.reHOzJzyZrM.LDfQdIWa3e/MkAu",
|
|
5
|
-
"webId": "http://localhost:3101/credtest/#me",
|
|
6
|
-
"podName": "credtest",
|
|
7
|
-
"createdAt": "2025-12-27T12:48:00.226Z",
|
|
8
|
-
"lastLogin": "2025-12-27T12:48:00.567Z"
|
|
9
|
-
}
|