Skip to content

Commit ad767e5

Browse files
author
Rajat
committed
Updated the examples
1 parent cb8c577 commit ad767e5

File tree

9 files changed

+435
-46
lines changed

9 files changed

+435
-46
lines changed

apps/web/app/account/billing/layout.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ import { redirect } from "next/navigation";
1111
import Script from "next/script";
1212
import { auth } from "@/auth";
1313

14-
export default async function SchoolDetailsLayout({
15-
children,
16-
}: {
17-
children: ReactNode;
18-
}) {
14+
const SchoolDetailsLayout = async ({ children }: { children: ReactNode }) => {
1915
const session = await auth();
2016

2117
if (!session) {
@@ -42,4 +38,6 @@ export default async function SchoolDetailsLayout({
4238
</main>
4339
</>
4440
);
45-
}
41+
};
42+
43+
export default SchoolDetailsLayout as any;

examples/express/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
MEDIALIT_API_KEY=your_api_key_here
2+
# Optional for self-hosted MediaLit
3+
# MEDIALIT_ENDPOINT=http://localhost:3001
4+
PORT=4000

examples/express/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
This is an Express example showing how to integrate the `medialit` SDK.
2+
3+
## Setup
4+
5+
1. Copy env file:
6+
7+
```bash
8+
cp .env.example .env
9+
```
10+
11+
2. Set your real API key in `.env`:
12+
13+
```bash
14+
MEDIALIT_API_KEY=your_api_key_here
15+
```
16+
17+
3. Install dependencies:
18+
19+
```bash
20+
pnpm install
21+
```
22+
23+
4. Run:
24+
25+
```bash
26+
pnpm dev
27+
```
28+
29+
## Routes
30+
31+
- `GET /health`
32+
- `POST /api/medialit/upload`
33+
- `GET /api/medialit`
34+
- `GET /api/medialit/:id`
35+
- `DELETE /api/medialit/:id`
36+
- `GET /api/medialit/count`
37+
- `POST /api/medialit/signature`
38+
39+
## Quick upload test
40+
41+
```bash
42+
curl -X POST http://localhost:4000/api/medialit/upload \
43+
-F "file=@/absolute/path/to/image.jpg" \
44+
-F "access=public" \
45+
-F "caption=My first upload"
46+
```

examples/express/package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "@example/express",
3+
"version": "0.1.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"dev": "node --env-file=.env server.js",
8+
"start": "node --env-file=.env server.js"
9+
},
10+
"dependencies": {
11+
"cors": "^2.8.5",
12+
"express": "^4.21.2",
13+
"medialit": "^0.2.0",
14+
"multer": "^1.4.5-lts.2"
15+
}
16+
}

examples/express/server.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import express from "express";
2+
import cors from "cors";
3+
import multer from "multer";
4+
import { readFile, unlink } from "node:fs/promises";
5+
import { MediaLit } from "medialit";
6+
7+
const app = express();
8+
const upload = multer({ dest: "uploads/" });
9+
const client = new MediaLit();
10+
11+
app.use(cors());
12+
app.use(express.json());
13+
14+
app.get("/health", (_req, res) => {
15+
res.json({ ok: true });
16+
});
17+
18+
// Upload via server
19+
app.post("/api/medialit/upload", upload.single("file"), async (req, res) => {
20+
try {
21+
if (!req.file) {
22+
return res.status(400).json({ error: "file is required" });
23+
}
24+
const apiKey = process.env.MEDIALIT_API_KEY;
25+
if (!apiKey) {
26+
return res
27+
.status(500)
28+
.json({ error: "MEDIALIT_API_KEY is missing" });
29+
}
30+
31+
const bytes = await readFile(req.file.path);
32+
const formData = new FormData();
33+
formData.append(
34+
"file",
35+
new Blob([bytes], { type: req.file.mimetype }),
36+
req.file.originalname || req.file.filename,
37+
);
38+
formData.append(
39+
"access",
40+
req.body.access === "public" ? "public" : "private",
41+
);
42+
if (req.body.caption) {
43+
formData.append("caption", req.body.caption);
44+
}
45+
if (req.body.group) {
46+
formData.append("group", req.body.group);
47+
}
48+
49+
const uploadResponse = await fetch(`${client.endpoint}/media/create`, {
50+
method: "POST",
51+
headers: {
52+
"x-medialit-apikey": apiKey,
53+
},
54+
body: formData,
55+
});
56+
const media = await uploadResponse.json();
57+
58+
if (!uploadResponse.ok) {
59+
throw new Error(media.error || "Upload failed");
60+
}
61+
if (!media.mediaId) {
62+
throw new Error("Upload succeeded but mediaId is missing");
63+
}
64+
65+
// Seal uploaded media so it persists and appears in list/get APIs.
66+
const sealedMedia = await client.seal(media.mediaId);
67+
68+
// Cleanup temp file once uploaded.
69+
await unlink(req.file.path).catch(() => undefined);
70+
71+
return res.json(sealedMedia);
72+
} catch (error) {
73+
return res.status(500).json({
74+
error: error instanceof Error ? error.message : "Unknown error",
75+
});
76+
}
77+
});
78+
79+
// List media
80+
app.get("/api/medialit", async (req, res) => {
81+
try {
82+
const page = Number.parseInt(String(req.query.page ?? "1"), 10);
83+
const limit = Number.parseInt(String(req.query.limit ?? "10"), 10);
84+
const access = req.query.access;
85+
const group = req.query.group;
86+
87+
const media = await client.list(page, limit, {
88+
access:
89+
access === "public" || access === "private"
90+
? access
91+
: undefined,
92+
group: typeof group === "string" ? group : undefined,
93+
});
94+
95+
return res.json(media);
96+
} catch (error) {
97+
return res.status(500).json({
98+
error: error instanceof Error ? error.message : "Unknown error",
99+
});
100+
}
101+
});
102+
103+
// Get media by id
104+
app.get("/api/medialit/:id", async (req, res) => {
105+
try {
106+
const media = await client.get(req.params.id);
107+
return res.json(media);
108+
} catch (error) {
109+
return res.status(500).json({
110+
error: error instanceof Error ? error.message : "Unknown error",
111+
});
112+
}
113+
});
114+
115+
// Delete media
116+
app.delete("/api/medialit/:id", async (req, res) => {
117+
try {
118+
await client.delete(req.params.id);
119+
return res.json({ success: true });
120+
} catch (error) {
121+
return res.status(500).json({
122+
error: error instanceof Error ? error.message : "Unknown error",
123+
});
124+
}
125+
});
126+
127+
// Count media
128+
app.get("/api/medialit/count", async (_req, res) => {
129+
try {
130+
const count = await client.getCount();
131+
return res.json({ count });
132+
} catch (error) {
133+
return res.status(500).json({
134+
error: error instanceof Error ? error.message : "Unknown error",
135+
});
136+
}
137+
});
138+
139+
// Optional: signature for browser direct upload
140+
app.post("/api/medialit/signature", async (req, res) => {
141+
try {
142+
const signature = await client.getSignature({
143+
group: req.body.group,
144+
});
145+
146+
return res.json({
147+
endpoint: client.endpoint,
148+
signature,
149+
});
150+
} catch (error) {
151+
return res.status(500).json({
152+
error: error instanceof Error ? error.message : "Unknown error",
153+
});
154+
}
155+
});
156+
157+
const port = Number(process.env.PORT ?? 4000);
158+
app.listen(port, () => {
159+
console.log(`Server running on http://localhost:${port}`);
160+
});

examples/next-app-router/app/api/medialit/route.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,28 @@ export async function DELETE(request: NextRequest) {
8080
);
8181
}
8282
}
83+
84+
export async function PATCH(request: NextRequest) {
85+
const searchParams = request.nextUrl.searchParams;
86+
const mediaId = searchParams.get("mediaId");
87+
88+
if (!mediaId) {
89+
return Response.json(
90+
{ error: "Media ID is required" },
91+
{ status: 400 },
92+
);
93+
}
94+
95+
try {
96+
const media = await client.seal(mediaId);
97+
return Response.json(media);
98+
} catch (error) {
99+
if (error instanceof Error) {
100+
return Response.json({ error: error.message }, { status: 500 });
101+
}
102+
return Response.json(
103+
{ error: "An unknown error occurred" },
104+
{ status: 500 },
105+
);
106+
}
107+
}

examples/next-app-router/components/MediaList.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export default function MediaList() {
5151
const [page, setPage] = useState(1);
5252
const [totalPages, setTotalPages] = useState(1);
5353
const [selectedMedia, setSelectedMedia] = useState<Media | null>(null);
54+
const [refreshKey, setRefreshKey] = useState(0);
5455
const itemsPerPage = 10;
5556

5657
useEffect(() => {
@@ -80,7 +81,13 @@ export default function MediaList() {
8081
};
8182

8283
fetchMedia();
83-
}, [page]);
84+
}, [page, refreshKey]);
85+
86+
useEffect(() => {
87+
const onRefresh = () => setRefreshKey((prev) => prev + 1);
88+
window.addEventListener("medialit:refresh", onRefresh);
89+
return () => window.removeEventListener("medialit:refresh", onRefresh);
90+
}, []);
8491

8592
const handleMediaClick = async (mediaId: string) => {
8693
try {

examples/next-app-router/components/MediaUploadForm.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ export default function MediaUploadForm() {
2222
const [error, setError] = useState<string>("");
2323
const [caption, setCaption] = useState("");
2424
const [isPublic, setIsPublic] = useState(false);
25+
const [isSealed, setIsSealed] = useState(false);
2526

2627
const handleUpload = async (e: React.FormEvent) => {
2728
e.preventDefault();
2829
if (!file) return;
2930

3031
setUploading(true);
3132
setError("");
33+
setIsSealed(false);
3234

3335
try {
3436
// Get presigned URL from our API route
@@ -71,6 +73,7 @@ export default function MediaUploadForm() {
7173

7274
if (mediaResponse.ok) {
7375
setUploadedMedia(mediaData);
76+
setIsSealed(false);
7477
} else {
7578
throw new Error(
7679
mediaData.error || "Failed to get media details",
@@ -87,27 +90,28 @@ export default function MediaUploadForm() {
8790
}
8891
};
8992

90-
const handleDelete = async () => {
93+
const handleSeal = async () => {
9194
if (!uploadedMedia?.mediaId) return;
9295

9396
try {
9497
const response = await fetch(
9598
`/api/medialit?mediaId=${uploadedMedia.mediaId}`,
9699
{
97-
method: "DELETE",
100+
method: "PATCH",
98101
},
99102
);
100103
const data = await response.json();
101104

102105
if (response.ok) {
103-
setUploadedMedia(null);
104-
setFile(null);
106+
setUploadedMedia(data);
107+
setIsSealed(true);
108+
window.dispatchEvent(new Event("medialit:refresh"));
105109
} else {
106-
throw new Error(data.error || "Failed to delete file");
110+
throw new Error(data.error || "Failed to seal file");
107111
}
108112
} catch (err) {
109113
setError(
110-
err instanceof Error ? err.message : "Failed to delete file",
114+
err instanceof Error ? err.message : "Failed to seal file",
111115
);
112116
}
113117
};
@@ -222,12 +226,14 @@ export default function MediaUploadForm() {
222226
</a>
223227
</div>
224228

225-
<button
226-
onClick={handleDelete}
227-
className="bg-red-500 text-white px-4 py-2 rounded-md hover:bg-red-600"
228-
>
229-
Delete File
230-
</button>
229+
{!isSealed ? (
230+
<button
231+
onClick={handleSeal}
232+
className="bg-emerald-600 text-white px-4 py-2 rounded-md hover:bg-emerald-700"
233+
>
234+
Seal File
235+
</button>
236+
) : null}
231237
</div>
232238
)}
233239
</div>

0 commit comments

Comments
 (0)