Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
# deepevents.ai
deepevents.ai main codebase

## Modules

- [`user-project-management`](./user-project-management) - runnable prototype for identity, researcher profiles, scientific project spaces, permissions, audit logs, and reputation metrics.
60 changes: 60 additions & 0 deletions user-project-management/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# User & Project Management

This module is a self-contained implementation for SCIBASE.AI issue #11. It models identity, researcher profiles, project spaces, permissions, sharing, audit logs, and reputation metrics for scientific collaboration.

## What It Covers

- Authentication through email, OAuth-style linked providers, ORCID, SAML, and MFA enforcement.
- Researcher profiles with ORCID sync, affiliations, keywords, activity, citation-style reputation metrics, and public/private profile mode.
- Scientific project spaces with documents, code, datasets, discussion threads, citations, funding metadata, and archive-ready status.
- Role-based access control for Owner, Admin, Contributor, Reviewer, and Viewer.
- Object-level permissions for project, documents, code, datasets, and review scopes.
- Time-limited collaborator invitations and project-level audit logs.
- Browser dashboard and JSON API for reviewer smoke testing.

## Run Locally

```bash
cd user-project-management
npm test
npm start
```

Then open `http://localhost:4130`.

## API Surface

- `GET /api/dashboard`
- `GET /api/auth/check`
- `GET /api/users/user-alice/orcid`
- `GET /api/users/user-alice/reputation`
- `GET /api/projects/create`
- `GET /api/access/check`

## Requirement Mapping

- Authentication and identity: implemented by `authenticateIdentity`.
- ORCID/OAuth/SAML/account linking: represented in user identity links and institutional SAML metadata.
- Researcher profiles: implemented by user records, `syncOrcidProfile`, activity, keywords, and profile visibility.
- Citation and reputation metrics: implemented by `computeReputation`.
- Project spaces: implemented by `createProjectSpace` and `demoWorkspace.projects`.
- Permissions and access control: implemented by `rolePermissions`, `evaluateAccess`, and object scopes.
- External collaborators and time-limited access: implemented by `inviteCollaborator`.
- Project audit log: implemented by per-project `auditLog`.

## Verification

```bash
npm test
node src/server.js
```

Optional smoke checks:

```bash
curl -s http://localhost:4130/api/dashboard
curl -s http://localhost:4130/api/auth/check
curl -s http://localhost:4130/api/access/check
```

Demo artifacts are committed under `docs/demo/`, including `dashboard.png` and `user-project-management-demo.mp4`.
6 changes: 6 additions & 0 deletions user-project-management/docs/demo-script.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Demo Script

1. Run `npm test` to verify authentication, ORCID sync, reputation, project creation, permissions, invitations, and dashboard payloads.
2. Run `npm start` and open `http://localhost:4130`.
3. Confirm the dashboard shows the project space, identity links, access matrix, and researcher profiles.
4. Smoke-test `/api/auth/check`, `/api/users/user-alice/reputation`, and `/api/access/check`.
Binary file added user-project-management/docs/demo/dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
14 changes: 14 additions & 0 deletions user-project-management/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@scibase/user-project-management",
"version": "0.1.0",
"private": true,
"description": "Self-contained user and project management prototype for SCIBASE.AI issue #11.",
"type": "module",
"scripts": {
"start": "node src/server.js",
"test": "node --test test/*.test.js"
},
"engines": {
"node": ">=20"
}
}
35 changes: 35 additions & 0 deletions user-project-management/public/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const dashboard = await fetch("/api/dashboard").then((response) => response.json());

document.querySelector("#projectTitle").textContent = dashboard.project.title;
document.querySelector("#project").innerHTML = [
row("Visibility", dashboard.project.visibility),
row("Resources", Object.values(dashboard.project.resources).flat().length),
row("Collaborators", dashboard.project.collaborators.length),
row("Audit events", dashboard.project.auditLog.length)
].join("");

document.querySelector("#identity").innerHTML = `
${row("Authenticated", dashboard.auth.authenticated)}
${row("Provider", dashboard.auth.provider)}
${row("Linked identities", dashboard.auth.identityLinks.join(", "))}
`;

document.querySelector("#access").innerHTML = dashboard.accessMatrix
.map((rule) => `<div class="rule"><strong>${rule.role}</strong><span>${rule.principalId}</span><small>${rule.permissions.join(", ")}</small></div>`)
.join("");

document.querySelector("#profiles").innerHTML = dashboard.profiles
.map(
(profile) => `
<div class="profile">
<strong>${profile.name}</strong>
<span>${profile.institution}</span>
<small>Score ${profile.reputation.score} · ${profile.orcid}</small>
</div>
`
)
.join("");

function row(label, value) {
return `<div class="row"><span>${label}</span><strong>${String(value)}</strong></div>`;
}
40 changes: 40 additions & 0 deletions user-project-management/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SCIBASE User Project Management</title>
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<main class="shell">
<header>
<p>SCIBASE.AI / issue #11</p>
<h1>User & Project Management</h1>
</header>
<section class="grid">
<article class="panel hero">
<p class="label">Project Space</p>
<h2 id="projectTitle">Loading...</h2>
<div id="project"></div>
</article>
<article class="panel">
<p class="label">Identity</p>
<h2>Authentication and ORCID sync</h2>
<div id="identity"></div>
</article>
<article class="panel">
<p class="label">Access Control</p>
<h2>Roles and object permissions</h2>
<div id="access"></div>
</article>
<article class="panel">
<p class="label">Profiles</p>
<h2>Reputation and activity</h2>
<div id="profiles"></div>
</article>
</section>
</main>
<script type="module" src="/app.js"></script>
</body>
</html>
119 changes: 119 additions & 0 deletions user-project-management/public/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
:root {
--ink: #161917;
--muted: #64716a;
--line: #d9e0dc;
--paper: #f5f2ec;
--panel: #ffffff;
--green: #176b4e;
--plum: #6f4568;
}

* {
box-sizing: border-box;
}

body {
margin: 0;
background: var(--paper);
color: var(--ink);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

.shell {
max-width: 1280px;
margin: 0 auto;
padding: 32px;
}

header {
display: flex;
align-items: end;
justify-content: space-between;
gap: 24px;
margin-bottom: 24px;
}

p,
h1,
h2 {
margin: 0;
}

header p,
.label {
color: var(--muted);
font-size: 12px;
font-weight: 900;
letter-spacing: 0.08em;
text-transform: uppercase;
}

h1 {
max-width: 760px;
font-size: clamp(38px, 6vw, 76px);
line-height: 0.95;
}

h2 {
margin-top: 8px;
font-size: 24px;
}

.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}

.panel {
min-height: 260px;
border: 1px solid var(--line);
background: var(--panel);
padding: 24px;
}

.hero {
grid-row: span 2;
}

.row,
.rule,
.profile {
border-top: 1px solid var(--line);
padding-top: 12px;
margin-top: 14px;
}

.row span,
.rule span,
.profile span,
.profile small,
.rule small {
display: block;
color: var(--muted);
line-height: 1.4;
}

.row strong,
.rule strong,
.profile strong {
display: block;
color: var(--green);
overflow-wrap: anywhere;
}

.rule strong {
color: var(--plum);
}

@media (max-width: 820px) {
.shell {
padding: 18px;
}

header,
.grid {
display: grid;
grid-template-columns: 1fr;
}
}
Loading