Skip to content

Commit 27532a2

Browse files
Merge pull request #24 from solid/feat/my-storages
Feat/my storages
2 parents 6a8377b + b306516 commit 27532a2

64 files changed

Lines changed: 19843 additions & 4234 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cssconfig.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^8.0.0/components/context.jsonld",
3+
"import": [
4+
"css:config/app/init/default.json",
5+
"css:config/app/main/default.json",
6+
"css:config/app/variables/default.json",
7+
"css:config/http/handler/default.json",
8+
"css:config/http/middleware/default.json",
9+
"css:config/http/notifications/all.json",
10+
"css:config/http/server-factory/http.json",
11+
"css:config/http/static/default.json",
12+
"css:config/identity/access/public.json",
13+
"css:config/identity/email/default.json",
14+
"css:config/identity/handler/no-accounts.json",
15+
"css:config/identity/oidc/default.json",
16+
"css:config/identity/ownership/token.json",
17+
"css:config/identity/pod/static.json",
18+
"css:config/ldp/authentication/dpop-bearer.json",
19+
"css:config/ldp/authorization/acp.json",
20+
"css:config/ldp/handler/default.json",
21+
"css:config/ldp/metadata-parser/default.json",
22+
"css:config/ldp/metadata-writer/default.json",
23+
"css:config/ldp/modes/default.json",
24+
"css:config/storage/backend/file.json",
25+
"css:config/storage/key-value/memory.json",
26+
"css:config/storage/location/root.json",
27+
"css:config/storage/middleware/default.json",
28+
"css:config/util/auxiliary/acr.json",
29+
"css:config/util/identifiers/suffix.json",
30+
"css:config/util/index/default.json",
31+
"css:config/util/logging/winston.json",
32+
"css:config/util/representation-conversion/default.json",
33+
"css:config/util/resource-locker/memory.json",
34+
"css:config/util/variables/default.json"
35+
]
36+
}

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ yarn-error.log*
3333
# env files (can opt-in for committing if needed)
3434
.env*
3535

36+
# seed config (contains passwords)
37+
seed-config.json
38+
src/ldo/
39+
3640
# vercel
3741
.vercel
3842

README-CSS.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Running Local Community Solid Server with ACP
2+
3+
## Setup
4+
5+
The project is configured to run a local Community Solid Server (CSS) with ACP (Access Control Policy) enabled.
6+
7+
## Configuration
8+
9+
- **Config file**: `.cssconfig.json` - Has ACP enabled via `css:config/ldp/authorization/acp.json`
10+
- **Seed config**: `seed-config.json` - Contains your account credentials (create from `seed-config.json.example`)
11+
- **Data directory**: `data/` - Where the server stores pod data
12+
13+
### Setting up your account
14+
15+
1. Copy the example seed config:
16+
```bash
17+
cp seed-config.json.example seed-config.json
18+
```
19+
20+
2. Edit `seed-config.json` and replace with your real email, password, and pod name:
21+
```json
22+
[
23+
{
24+
"email": "your-email@example.com",
25+
"password": "your-password",
26+
"pods": [
27+
{ "name": "your-pod-name" }
28+
]
29+
}
30+
]
31+
```
32+
33+
**Note**: `seed-config.json` is in `.gitignore` to prevent committing your password.
34+
35+
## Running the Server
36+
37+
### Option 1: Run CSS only
38+
```bash
39+
npm run start:css
40+
```
41+
42+
This will start CSS on port **3000** with:
43+
- ACP authorization enabled
44+
- Seeded accounts (alice and bob)
45+
- Data stored in `data/` directory
46+
47+
### Option 2: Run CSS + Next.js together
48+
```bash
49+
npm run start:dev
50+
```
51+
52+
This runs both:
53+
- CSS on port **3000**
54+
- Next.js dev server on port **3001**
55+
56+
## Test Accounts
57+
58+
After seeding, you can log in with any of these accounts:
59+
60+
1. **Ruky** (your account):
61+
- Email: `rukyjacob@gmail.com`
62+
- Password: `Test123$`
63+
- Pod URL: `http://localhost:3000/ruky/`
64+
- WebID: `http://localhost:3000/ruky/profile/card#me`
65+
66+
2. **Alice**:
67+
- Email: `alice@example.com`
68+
- Password: `alice123`
69+
- Pod URL: `http://localhost:3000/alice/`
70+
- WebID: `http://localhost:3000/alice/profile/card#me`
71+
72+
3. **Bob**:
73+
- Email: `bob@example.com`
74+
- Password: `bob123`
75+
- Pod URL: `http://localhost:3000/bob/`
76+
- WebID: `http://localhost:3000/bob/profile/card#me`
77+
78+
4. **Charlie**:
79+
- Email: `charlie@example.com`
80+
- Password: `charlie123`
81+
- Pod URL: `http://localhost:3000/charlie/`
82+
- WebID: `http://localhost:3000/charlie/profile/card#me`
83+
84+
## Testing ACP Sharing
85+
86+
The seed config includes 4 test accounts (ruky, alice, bob, charlie) for testing sharing.
87+
88+
**Steps to test:**
89+
1. Start the server: `npm run start:css`
90+
2. In your Next.js app (port 3001), log in as **ruky** (`rukyjacob@gmail.com` / `Test123$`)
91+
3. Navigate to ruky's pod storage
92+
4. Create a folder or file
93+
5. Click "Share" on the resource
94+
6. Add a WebID to share with (e.g., `http://localhost:3000/alice/profile/card#me`)
95+
7. Select access level (Editor/Viewer) and click "Done"
96+
97+
**Quick WebIDs for sharing:**
98+
- Alice: `http://localhost:3000/alice/profile/card#me`
99+
- Bob: `http://localhost:3000/bob/profile/card#me`
100+
- Charlie: `http://localhost:3000/charlie/profile/card#me`
101+
102+
## Notes
103+
104+
- The server uses ACP (not WAC), so ACRs should be at `.acr` location
105+
- Make sure to use `http://localhost:3000` as the OIDC issuer in your app
106+
- The server will create pods automatically when accounts are seeded
107+

README.md

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ This application provides a user-friendly interface for managing files and folde
4747
- **Google Drive-like Interface**: Clean, modern interface with familiar UX patterns
4848
- **Left Sidebar**: Displays all available file manager menus
4949
- **File Item Display**: Shows files and folders with appropriate icons, metadata, and context menus
50-
- **Context Menus**: Hover on folder/file and click the menu button to access the file operations menu dropdown (Rename, Copy, Move, Delete, Preview, Download)
50+
- **Context Menus**: Hover on folder/file and click the menu button to access the file operations menu dropdown (Rename, Copy, Move, Delete, Preview, Download, Share)
5151
- **Modals & Dialogs**:
5252
- Rename dialog for renaming resources
5353
- Move dialog for selecting destination folder
5454
- Preview modal for viewing files
55-
- Permissions dialog (UI ready)
55+
- Share dialog for sharing files/folders with WebIDs (with contact autocomplete)
56+
- Share success modal for displaying shared resource URLs
5657
- **Loading States**: Loading spinners and error displays throughout
5758
- **Toast Notifications**: User feedback for all operations
5859
- **Minimal Design**: Black, white, and light purple color scheme
@@ -64,8 +65,10 @@ This application provides a user-friendly interface for managing files and folde
6465
- **Profile Utilities**: WebID profile fetching and parsing utilities
6566
- **URL Utilities**: URL state management, encoding/decoding, and path resolution
6667
- **Binary File Detection**: Automatic detection of binary/system files (e.g., `.DS_Store`) to prevent unnecessary RDF conversion attempts
68+
- **ACP Utilities**: ACP sharing operations using LDO for type-safe RDF parsing and N3.js for ACR creation/updates
69+
- **Contact Utilities**: Fetching and parsing user contacts from WebID profiles for sharing autocomplete
6770
- **Error Handling**: Comprehensive error handling with user-friendly messages
68-
- **Type Safety**: Full TypeScript support throughout
71+
- **Type Safety**: Full TypeScript support throughout, including LDO-generated types from SHACL shapes
6972
- **Cache-Busting**: Automatic cache-busting for container listings after uploads/deletes to ensure fresh data
7073

7174
## Tech Stack
@@ -75,6 +78,7 @@ This application provides a user-friendly interface for managing files and folde
7578
- **Styling**: Tailwind CSS 4
7679
- **Language**: TypeScript
7780
- **Solid SDK**: [@inrupt/solid-client-js](https://github.com/inrupt/solid-client-js)
81+
- **LDO (Linked Data Objects)**: [@ldo/ldo](https://github.com/o-development/ldo) for type-safe ACP operations
7882
- **Icons**: [@heroicons/react](https://heroicons.com/)
7983
- **Notifications**: [react-hot-toast](https://react-hot-toast.com/)
8084

@@ -98,12 +102,19 @@ cd solid-file-manager
98102
npm install
99103
```
100104

101-
3. Run the development server:
105+
3. Build LDO types (automatically runs before dev server):
106+
```bash
107+
npm run build:ldo
108+
```
109+
110+
4. Run the development server:
102111
```bash
103112
npm run dev
104113
```
105114

106-
4. Open [http://localhost:3001](http://localhost:3001) in your browser.
115+
5. Open [http://localhost:3001](http://localhost:3001) in your browser.
116+
117+
**Note**: For ACP sharing functionality, you'll need to run a local Community Solid Server. See `README-CSS.md` for setup instructions.
107118

108119
## Development Setup
109120

@@ -146,8 +157,8 @@ solid-file-manager/
146157
│ │ ├── NewFolderDialog.tsx # Dialog for creating folders
147158
│ │ ├── RenameDialog.tsx # Dialog for renaming resources
148159
│ │ ├── MoveDialog.tsx # Dialog for moving files
149-
│ │ ├── PreviewModal.tsx # Modal for previewing files
150-
│ │ ├── PermissionsDialog.tsx # Sharing/permissions dialog
160+
│ │ ├── ShareDialog.tsx # Dialog for sharing files/folders with WebIDs
161+
│ │ ├── ShareSuccessModal.tsx # Modal showing success after sharing
151162
│ │ ├── FileUploadHandler.tsx # File upload component
152163
│ │ ├── ProfileIcon.tsx # User profile icon and logout
153164
│ │ ├── LoginPage.tsx # Login page
@@ -173,20 +184,40 @@ solid-file-manager/
173184
│ │ ├── urlStateUtils.ts # URL state management
174185
│ │ ├── urlUtils.ts # URL utilities and binary file detection
175186
│ │ ├── fileTypeUtils.ts # File type detection
187+
│ │ ├── acpUtils.ts # ACP sharing utilities (LDO + N3.js)
188+
│ │ ├── contactUtils.ts # Contact fetching for sharing
176189
│ │ └── ...
177190
│ ├── page.tsx # Main page component
178191
│ ├── layout.tsx # Root layout
179192
│ └── globals.css # Global styles
193+
├── src/
194+
│ ├── shapes/ # SHACL Compact Syntax shape definitions
195+
│ │ └── Model.shaclc # ACP shape definitions
196+
│ └── ldo/ # Generated LDO types (auto-generated)
197+
│ ├── Model.typings.ts # TypeScript interfaces
198+
│ ├── Model.shapeTypes.ts # Shape type definitions
199+
│ ├── Model.context.ts # JSON-LD context
200+
│ └── Model.schema.ts # ShEx schema
201+
├── scripts/
202+
│ ├── fix-shex.js # Post-process ShEx output
203+
│ └── generate-ldo-files.js # Generate LDO files from ShEx
180204
├── public/ # Static assets
181-
└── README.md
205+
├── README.md
206+
└── README-CSS.md # Local CSS setup instructions
182207
```
183208

184209
## Solid Protocol Integration
185210

186211
This application integrates with Solid using:
187212

188213
- **Solid Protocol**: [https://solidproject.org/TR/protocol#resources](https://solidproject.org/TR/protocol#resources)
189-
- **ACP (Access Control Policies)**: [https://solid.github.io/authorization-panel/acp-specification/](https://solid.github.io/authorization-panel/acp-specification/) (UI ready, full integration pending)
214+
- **ACP (Access Control Policies)**: [https://solid.github.io/authorization-panel/acp-specification/](https://solid.github.io/authorization-panel/acp-specification/)
215+
- Full ACP sharing implementation with WebID-based access control
216+
- Uses LDO (Linked Data Objects) for type-safe ACR parsing
217+
- Uses N3.js for ACR creation and updates
218+
- Supports Editor (Read + Write) and Viewer (Read) access levels
219+
- Automatic ACR discovery via Link headers or `.acr` convention
220+
- Contact autocomplete from WebID profiles (`foaf:knows`)
190221
- **Storage Root Discovery**:
191222
- Uses `pim:storage` predicate from WebID profile
192223
- Uses `solid:storage` predicate as fallback
@@ -200,6 +231,11 @@ This application integrates with Solid using:
200231
- Recursive folder operations for copy, move, and delete
201232
- Automatic handling of binary files and system files
202233
- **SDK**: [@inrupt/solid-client-js](https://github.com/inrupt/solid-client-js)
234+
- **LDO Type Generation**:
235+
- SHACL Compact Syntax shapes defined in `src/shapes/Model.shaclc`
236+
- Automatically converted to ShEx via `@jeswr/shacl2shex`
237+
- TypeScript types generated via `@ldo/cli` and custom scripts
238+
- Types are regenerated on `npm run dev` and `npm run build`
203239

204240
## Design Principles
205241

@@ -214,6 +250,7 @@ This application integrates with Solid using:
214250
- [Solid Protocol Specification](https://solidproject.org/TR/protocol)
215251
- [ACP Specification](https://solid.github.io/authorization-panel/acp-specification/)
216252
- [Inrupt Solid Client JS](https://github.com/inrupt/solid-client-js)
253+
- [LDO (Linked Data Objects)](https://github.com/o-development/ldo)
217254
- [Community Solid Server](https://github.com/CommunitySolidServer/CommunitySolidServer)
218255

219256
## License

app/components/FileItem.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface FileItemProps {
2727
onMove?: (file: FileItemData) => void;
2828
onDownload?: (file: FileItemData) => void;
2929
onDelete?: (file: FileItemData) => void;
30+
onShare?: (file: FileItemData) => void;
3031
isSelected?: boolean;
3132
onContextMenu?: (file: FileItemData, event: React.MouseEvent) => void;
3233
}
@@ -42,6 +43,7 @@ export default function FileItem({
4243
onMove,
4344
onDownload,
4445
onDelete,
46+
onShare,
4547
isSelected = false,
4648
onContextMenu,
4749
}: FileItemProps) {
@@ -146,6 +148,7 @@ export default function FileItem({
146148
onCopy={onCopy}
147149
onMove={onMove}
148150
onDelete={onDelete}
151+
onShare={onShare}
149152
/>
150153
)}
151154
<div className="mb-1 flex h-12 w-12 items-center justify-center sm:mb-2 sm:h-16 sm:w-16">
@@ -199,6 +202,7 @@ export default function FileItem({
199202
onCopy={onCopy}
200203
onMove={onMove}
201204
onDelete={onDelete}
205+
onShare={onShare}
202206
/>
203207
)}
204208
</section>

app/components/FileItemMenu.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
ArrowRightCircleIcon,
1111
TrashIcon,
1212
EyeIcon,
13+
ShareIcon,
1314
} from "@heroicons/react/24/outline";
1415
import { useClickOutside } from "../lib/hooks";
1516
import { FileItemData } from "./FileItem";
@@ -22,6 +23,7 @@ interface FileItemMenuProps {
2223
onMove?: (file: FileItemData) => void;
2324
onDelete?: (file: FileItemData) => void;
2425
onPreview?: (file: FileItemData) => void;
26+
onShare?: (file: FileItemData) => void;
2527
position?: "top-right" | "right";
2628
}
2729

@@ -33,6 +35,7 @@ export default function FileItemMenu({
3335
onMove,
3436
onDelete,
3537
onPreview,
38+
onShare,
3639
position = "right",
3740
}: FileItemMenuProps) {
3841
const [showMenu, setShowMenu] = useState(false);
@@ -104,6 +107,13 @@ export default function FileItemMenu({
104107
className: "text-gray-700 hover:bg-gray-100 border-b border-gray-100",
105108
iconClassName: "text-gray-500",
106109
},
110+
{
111+
label: "Share",
112+
icon: ShareIcon,
113+
action: onShare,
114+
className: "text-gray-700 hover:bg-gray-100 border-b border-gray-100",
115+
iconClassName: "text-gray-500",
116+
},
107117
// Only show Move for files, not folders
108118
...(file.type === "file"
109119
? [

app/components/FileList.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ interface FileListProps {
1616
onFileMove?: (file: FileItemData) => void;
1717
onFileDownload?: (file: FileItemData) => void;
1818
onFileDelete?: (file: FileItemData) => void;
19+
onFileShare?: (file: FileItemData) => void;
1920
selectedFileIds: string[];
2021
onFileContextMenu?: (file: FileItemData, event: React.MouseEvent) => void;
2122
}
@@ -33,6 +34,7 @@ export default function FileList({
3334
onFileMove,
3435
onFileDownload,
3536
onFileDelete,
37+
onFileShare,
3638
selectedFileIds,
3739
onFileContextMenu,
3840
}: FileListProps) {
@@ -73,6 +75,7 @@ export default function FileList({
7375
onMove={onFileMove}
7476
onDownload={onFileDownload}
7577
onDelete={onFileDelete}
78+
onShare={onFileShare}
7679
isSelected={selectedFileIds.includes(file.id)}
7780
onContextMenu={onFileContextMenu}
7881
/>
@@ -93,6 +96,7 @@ export default function FileList({
9396
onMove={onFileMove}
9497
onDownload={onFileDownload}
9598
onDelete={onFileDelete}
99+
onShare={onFileShare}
96100
isSelected={selectedFileIds.includes(file.id)}
97101
onContextMenu={onFileContextMenu}
98102
/>

0 commit comments

Comments
 (0)