@vilio/media-module 0.0.2
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/dist/api/delete.d.ts +7 -0
- package/dist/api/delete.d.ts.map +1 -0
- package/dist/api/delete.js +35 -0
- package/dist/api/delete.js.map +1 -0
- package/dist/api/list.d.ts +19 -0
- package/dist/api/list.d.ts.map +1 -0
- package/dist/api/list.js +23 -0
- package/dist/api/list.js.map +1 -0
- package/dist/api/upload.d.ts +19 -0
- package/dist/api/upload.d.ts.map +1 -0
- package/dist/api/upload.js +50 -0
- package/dist/api/upload.js.map +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/components/MediaPicker.d.ts +16 -0
- package/dist/components/MediaPicker.d.ts.map +1 -0
- package/dist/components/MediaPicker.js +76 -0
- package/dist/components/MediaPicker.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/pages/MediaPage.d.ts +2 -0
- package/dist/pages/MediaPage.d.ts.map +1 -0
- package/dist/pages/MediaPage.js +95 -0
- package/dist/pages/MediaPage.js.map +1 -0
- package/dist/routes.d.ts +4 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +31 -0
- package/dist/routes.js.map +1 -0
- package/dist/schema.d.ts +806 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +42 -0
- package/dist/schema.js.map +1 -0
- package/locales/en/global.json +20 -0
- package/locales/pl/global.json +20 -0
- package/manifest.json +11 -0
- package/package.json +44 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../src/api/delete.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO;;;;IAmC5C"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { db, getCurrentSession } from "@vilio/core/server";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { NextResponse } from "next/server";
|
|
4
|
+
import { mediaTable } from "../schema";
|
|
5
|
+
export async function DELETE(request) {
|
|
6
|
+
try {
|
|
7
|
+
const session = await getCurrentSession();
|
|
8
|
+
if (!session?.user) {
|
|
9
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
10
|
+
}
|
|
11
|
+
const { searchParams } = new URL(request.url);
|
|
12
|
+
const id = searchParams.get("id");
|
|
13
|
+
if (!id) {
|
|
14
|
+
return NextResponse.json({ error: "No ID provided" }, { status: 400 });
|
|
15
|
+
}
|
|
16
|
+
const [item] = await db
|
|
17
|
+
.select()
|
|
18
|
+
.from(mediaTable)
|
|
19
|
+
.where(eq(mediaTable.id, id))
|
|
20
|
+
.limit(1);
|
|
21
|
+
if (!item) {
|
|
22
|
+
return NextResponse.json({ error: "Media not found" }, { status: 404 });
|
|
23
|
+
}
|
|
24
|
+
// Use filesystemService to delete from the correct provider
|
|
25
|
+
//await filesystemService.delete(item.url, item.provider as any);
|
|
26
|
+
// Remove from DB
|
|
27
|
+
await db.delete(mediaTable).where(eq(mediaTable.id, id));
|
|
28
|
+
return NextResponse.json({ success: true });
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error("[Media:Delete] Error:", error);
|
|
32
|
+
return NextResponse.json({ error: "Delete failed" }, { status: 500 });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/api/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAqB,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,OAAgB;IAC3C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAC1C,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;YACnB,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAElC,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,EAAE;aACpB,MAAM,EAAE;aACR,IAAI,CAAC,UAAU,CAAC;aAChB,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;aAC5B,KAAK,CAAC,CAAC,CAAC,CAAC;QAEZ,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED,4DAA4D;QAC5D,iEAAiE;QAEjE,iBAAiB;QACjB,MAAM,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAEzD,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;QAC9C,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACxE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
export declare function GET(): Promise<NextResponse<{
|
|
3
|
+
error: string;
|
|
4
|
+
}> | NextResponse<{
|
|
5
|
+
id: string;
|
|
6
|
+
filename: string;
|
|
7
|
+
url: string;
|
|
8
|
+
provider: "local" | "vercel-blob" | "s3";
|
|
9
|
+
mimeType: string;
|
|
10
|
+
size: number;
|
|
11
|
+
width: number | null;
|
|
12
|
+
height: number | null;
|
|
13
|
+
alt: string | null;
|
|
14
|
+
metadata: unknown;
|
|
15
|
+
authorId: string | null;
|
|
16
|
+
createdAt: Date;
|
|
17
|
+
updatedAt: Date | null;
|
|
18
|
+
}[]>>;
|
|
19
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/api/list.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,wBAAsB,GAAG;;;;;;;;;;;;;;;;MAqBxB"}
|
package/dist/api/list.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { db, getCurrentSession } from "@vilio/core/server";
|
|
2
|
+
import { desc } from "drizzle-orm";
|
|
3
|
+
import { NextResponse } from "next/server";
|
|
4
|
+
import { mediaTable } from "../schema";
|
|
5
|
+
export async function GET() {
|
|
6
|
+
try {
|
|
7
|
+
const session = await getCurrentSession();
|
|
8
|
+
if (!session?.user) {
|
|
9
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
10
|
+
}
|
|
11
|
+
const items = await db
|
|
12
|
+
.select()
|
|
13
|
+
.from(mediaTable)
|
|
14
|
+
.orderBy(desc(mediaTable.createdAt))
|
|
15
|
+
.limit(100);
|
|
16
|
+
return NextResponse.json(items);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.error("[Media:List] Error:", error);
|
|
20
|
+
return NextResponse.json({ error: "Failed to fetch media" }, { status: 500 });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/api/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,GAAG;IACvB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAC1C,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;YACnB,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,EAAE;aACnB,MAAM,EAAE;aACR,IAAI,CAAC,UAAU,CAAC;aAChB,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;aACnC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEd,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;QAC5C,OAAO,YAAY,CAAC,IAAI,CACtB,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAClC,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
export declare function POST(request: Request): Promise<NextResponse<{
|
|
3
|
+
error: string;
|
|
4
|
+
}> | NextResponse<{
|
|
5
|
+
metadata: unknown;
|
|
6
|
+
id: string;
|
|
7
|
+
url: string;
|
|
8
|
+
size: number;
|
|
9
|
+
height: number | null;
|
|
10
|
+
width: number | null;
|
|
11
|
+
alt: string | null;
|
|
12
|
+
filename: string;
|
|
13
|
+
provider: "local" | "vercel-blob" | "s3";
|
|
14
|
+
mimeType: string;
|
|
15
|
+
authorId: string | null;
|
|
16
|
+
createdAt: Date;
|
|
17
|
+
updatedAt: Date | null;
|
|
18
|
+
}>>;
|
|
19
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO;;;;;;;;;;;;;;;;IAuD1C"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { db, filesystemService, getCurrentSession } from "@vilio/core/server";
|
|
2
|
+
import { applyFilters } from "@vilio/modules/server";
|
|
3
|
+
import { NextResponse } from "next/server";
|
|
4
|
+
import { mediaTable } from "../schema";
|
|
5
|
+
export async function POST(request) {
|
|
6
|
+
try {
|
|
7
|
+
const session = await getCurrentSession();
|
|
8
|
+
if (!session?.user) {
|
|
9
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
10
|
+
}
|
|
11
|
+
const formData = await request.formData();
|
|
12
|
+
const file = formData.get("file");
|
|
13
|
+
if (!file) {
|
|
14
|
+
return NextResponse.json({ error: "No file provided" }, { status: 400 });
|
|
15
|
+
}
|
|
16
|
+
// Use Vilio Filesystem Service to handle upload (local, vercel-blob, etc.)
|
|
17
|
+
const result = await filesystemService.upload(file);
|
|
18
|
+
// Type guard: Check if the result is an error
|
|
19
|
+
if ("error" in result) {
|
|
20
|
+
console.error("[Media:Upload] Filesystem error:", result.error);
|
|
21
|
+
return NextResponse.json({ error: result.error }, { status: 500 });
|
|
22
|
+
}
|
|
23
|
+
// result is now inferred as StorageData (has url, service, etc.)
|
|
24
|
+
const uploaded = result;
|
|
25
|
+
// Ensure we have a URL before inserting into DB
|
|
26
|
+
if (!uploaded.url) {
|
|
27
|
+
console.error("[Media:Upload] Provider returned empty URL:", uploaded);
|
|
28
|
+
return NextResponse.json({ error: "Provider did not return a file URL" }, { status: 500 });
|
|
29
|
+
}
|
|
30
|
+
// --- HOOK: media:upload:before_db ---
|
|
31
|
+
// Allow other modules to modify media data before it's saved to DB
|
|
32
|
+
const mediaData = await applyFilters("media:upload:before_db", {
|
|
33
|
+
filename: uploaded.name || file.name,
|
|
34
|
+
url: uploaded.url,
|
|
35
|
+
provider: (uploaded.service || "local"),
|
|
36
|
+
mimeType: uploaded.type || file.type,
|
|
37
|
+
size: uploaded.size || file.size,
|
|
38
|
+
authorId: session.user.id,
|
|
39
|
+
metadata: uploaded.metadata || {},
|
|
40
|
+
});
|
|
41
|
+
// Insert into DB
|
|
42
|
+
const [media] = await db.insert(mediaTable).values(mediaData).returning();
|
|
43
|
+
return NextResponse.json(media);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error("[Media:Upload] Unexpected Error:", error);
|
|
47
|
+
return NextResponse.json({ error: "Upload failed" }, { status: 500 });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface MediaItem {
|
|
3
|
+
id: string;
|
|
4
|
+
filename: string;
|
|
5
|
+
url: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
size: number;
|
|
8
|
+
}
|
|
9
|
+
interface MediaPickerProps {
|
|
10
|
+
onSelect: (item: MediaItem) => void;
|
|
11
|
+
trigger?: React.ReactNode;
|
|
12
|
+
selectedId?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function MediaPicker({ onSelect, trigger, selectedId, }: MediaPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=MediaPicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaPicker.d.ts","sourceRoot":"","sources":["../../src/components/MediaPicker.tsx"],"names":[],"mappings":"AAeA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,gBAAgB;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACpC,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAO,EACP,UAAU,GACX,EAAE,gBAAgB,2CAyKlB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { i18n } from "@vilio/intl";
|
|
4
|
+
import { Button } from "@vilio/ui/components/button";
|
|
5
|
+
import { Card } from "@vilio/ui/components/card";
|
|
6
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from "@vilio/ui/components/dialog";
|
|
7
|
+
import { Input } from "@vilio/ui/components/input";
|
|
8
|
+
import { Skeleton } from "@vilio/ui/components/skeleton";
|
|
9
|
+
import { Check, Search, Upload } from "lucide-react";
|
|
10
|
+
import { useEffect, useState } from "react";
|
|
11
|
+
import { toast } from "sonner";
|
|
12
|
+
export function MediaPicker({ onSelect, trigger, selectedId, }) {
|
|
13
|
+
const [items, setItems] = useState([]);
|
|
14
|
+
const [loading, setLoading] = useState(false);
|
|
15
|
+
const [uploading, setUploading] = useState(false);
|
|
16
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
17
|
+
const [open, setOpen] = useState(false);
|
|
18
|
+
const fetchMedia = async () => {
|
|
19
|
+
try {
|
|
20
|
+
setLoading(true);
|
|
21
|
+
const res = await fetch("/api/media");
|
|
22
|
+
const data = await res.json();
|
|
23
|
+
if (Array.isArray(data)) {
|
|
24
|
+
setItems(data);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
toast.error("Failed to load media");
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
setLoading(false);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (open) {
|
|
36
|
+
fetchMedia();
|
|
37
|
+
}
|
|
38
|
+
}, [open]);
|
|
39
|
+
const handleUpload = async (e) => {
|
|
40
|
+
const file = e.target.files?.[0];
|
|
41
|
+
if (!file)
|
|
42
|
+
return;
|
|
43
|
+
const formData = new FormData();
|
|
44
|
+
formData.append("file", file);
|
|
45
|
+
try {
|
|
46
|
+
setUploading(true);
|
|
47
|
+
const res = await fetch("/api/media/upload", {
|
|
48
|
+
method: "POST",
|
|
49
|
+
body: formData,
|
|
50
|
+
});
|
|
51
|
+
if (res.ok) {
|
|
52
|
+
const newMedia = await res.json();
|
|
53
|
+
toast.success("File uploaded successfully");
|
|
54
|
+
// Refresh list and select the new item
|
|
55
|
+
await fetchMedia();
|
|
56
|
+
handleSelect(newMedia);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
toast.error("Upload failed");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
toast.error("An error occurred during upload");
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
setUploading(false);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const filteredItems = items.filter((item) => item.filename.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
70
|
+
const handleSelect = (item) => {
|
|
71
|
+
onSelect(item);
|
|
72
|
+
setOpen(false);
|
|
73
|
+
};
|
|
74
|
+
return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: trigger || _jsx(Button, { variant: "outline", children: i18n("Select Media") }) }), _jsxs(DialogContent, { className: "max-w-4xl max-h-[90vh] flex flex-col p-0 overflow-hidden", children: [_jsx(DialogHeader, { className: "p-6 pb-2", children: _jsxs(DialogTitle, { className: "flex items-center justify-between", children: [_jsx("span", { children: i18n("Media Library") }), _jsx("div", { className: "flex items-center gap-2", children: _jsxs("label", { className: "cursor-pointer", children: [_jsx("input", { type: "file", className: "hidden", onChange: handleUpload, disabled: uploading }), _jsx(Button, { size: "sm", variant: "default", asChild: true, disabled: uploading, children: _jsxs("span", { children: [_jsx(Upload, { className: "mr-2 h-4 w-4" }), uploading ? i18n("Uploading...") : i18n("Upload New")] }) })] }) })] }) }), _jsx("div", { className: "px-6 py-2 border-b bg-muted/30", children: _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), _jsx(Input, { placeholder: i18n("Search files..."), className: "pl-10 bg-background", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value) })] }) }), _jsx("div", { className: "flex-1 overflow-y-auto p-6 min-h-[400px]", children: loading && items.length === 0 ? (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4", children: Array.from({ length: 12 }).map((_, i) => (_jsx(Skeleton, { className: "aspect-square rounded-lg" }, i))) })) : filteredItems.length > 0 ? (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4", children: filteredItems.map((item) => (_jsxs(Card, { className: `group relative aspect-square overflow-hidden cursor-pointer hover:ring-2 hover:ring-primary transition-all border-none bg-muted/50 ${selectedId === item.id ? "ring-2 ring-primary" : ""}`, onClick: () => handleSelect(item), children: [item.mimeType.startsWith("image/") ? (_jsx("img", { src: item.url, alt: item.filename, className: "h-full w-full object-cover" })) : (_jsx("div", { className: "flex h-full items-center justify-center bg-muted", children: _jsx("div", { className: "text-xs font-medium uppercase text-muted-foreground", children: item.mimeType.split("/")[1] }) })), selectedId === item.id && (_jsx("div", { className: "absolute inset-0 bg-primary/20 flex items-center justify-center", children: _jsx("div", { className: "bg-primary text-white rounded-full p-1 shadow-lg", children: _jsx(Check, { className: "h-4 w-4" }) }) })), _jsx("div", { className: "absolute inset-x-0 bottom-0 bg-black/60 p-2 opacity-0 group-hover:opacity-100 transition-opacity", children: _jsx("p", { className: "text-[10px] truncate text-white text-center font-medium", children: item.filename }) })] }, item.id))) })) : (_jsxs("div", { className: "flex flex-col items-center justify-center py-20 text-center text-muted-foreground", children: [_jsx(Upload, { className: "h-10 w-10 mb-4 opacity-20" }), _jsx("p", { className: "text-sm", children: i18n("No media found. Upload your first file!") })] })) })] })] }));
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=MediaPicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaPicker.js","sourceRoot":"","sources":["../../src/components/MediaPicker.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAO9C,QAAA,MAAM,MAAM,EAAE,OAqCb,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { i18n } from "@vilio/intl";
|
|
2
|
+
import localesEn from "../locales/en/global.json" with { type: "json" };
|
|
3
|
+
import localesPl from "../locales/pl/global.json" with { type: "json" };
|
|
4
|
+
import manifest from "../manifest.json";
|
|
5
|
+
import { apiRoutes, privateRoutes } from "./routes";
|
|
6
|
+
import { mediaSchema, relations } from "./schema";
|
|
7
|
+
const module = {
|
|
8
|
+
manifest,
|
|
9
|
+
translations: {
|
|
10
|
+
en: localesEn,
|
|
11
|
+
pl: localesPl,
|
|
12
|
+
},
|
|
13
|
+
onEnable: async () => {
|
|
14
|
+
console.log("[Module:Media] enabled");
|
|
15
|
+
},
|
|
16
|
+
onDisable: async () => {
|
|
17
|
+
console.log("[Module:Media] disabled");
|
|
18
|
+
},
|
|
19
|
+
schema: {
|
|
20
|
+
...mediaSchema,
|
|
21
|
+
relations,
|
|
22
|
+
},
|
|
23
|
+
routes: {
|
|
24
|
+
private: privateRoutes,
|
|
25
|
+
api: apiRoutes,
|
|
26
|
+
},
|
|
27
|
+
navigation: {
|
|
28
|
+
admin: {
|
|
29
|
+
[i18n("General")]: [
|
|
30
|
+
{
|
|
31
|
+
id: "media-library",
|
|
32
|
+
title: i18n("Media Library"),
|
|
33
|
+
url: "/media",
|
|
34
|
+
icon: "solar:album-bold-duotone",
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export default module;
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,OAAO,SAAS,MAAM,2BAA2B,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACxE,OAAO,SAAS,MAAM,2BAA2B,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACxE,OAAO,QAAQ,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAElD,MAAM,MAAM,GAAY;IACtB,QAAQ;IACR,YAAY,EAAE;QACZ,EAAE,EAAE,SAAS;QACb,EAAE,EAAE,SAAS;KACd;IAED,QAAQ,EAAE,KAAK,IAAI,EAAE;QACnB,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACxC,CAAC;IAED,SAAS,EAAE,KAAK,IAAI,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,EAAE;QACN,GAAG,WAAW;QACd,SAAS;KACV;IAED,MAAM,EAAE;QACN,OAAO,EAAE,aAAa;QACtB,GAAG,EAAE,SAAS;KACf;IAED,UAAU,EAAE;QACV,KAAK,EAAE;YACL,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE;gBACjB;oBACE,EAAE,EAAE,eAAe;oBACnB,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC;oBAC5B,GAAG,EAAE,QAAQ;oBACb,IAAI,EAAE,0BAA0B;iBACjC;aACF;SACF;KACF;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaPage.d.ts","sourceRoot":"","sources":["../../src/pages/MediaPage.tsx"],"names":[],"mappings":"AAgCA,MAAM,CAAC,OAAO,UAAU,SAAS,4CA+LhC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** biome-ignore-all lint/correctness/useExhaustiveDependencies: <> */
|
|
2
|
+
/** biome-ignore-all lint/suspicious/noArrayIndexKey: <> */
|
|
3
|
+
/** biome-ignore-all lint/a11y/useButtonType: <> */
|
|
4
|
+
/** biome-ignore-all lint/performance/noImgElement: <> */
|
|
5
|
+
"use client";
|
|
6
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useTranslation } from "@vilio/intl";
|
|
8
|
+
import { Button } from "@vilio/ui/components/button";
|
|
9
|
+
import { Card } from "@vilio/ui/components/card";
|
|
10
|
+
import { Input } from "@vilio/ui/components/input";
|
|
11
|
+
import { Skeleton } from "@vilio/ui/components/skeleton";
|
|
12
|
+
import { ExternalLink, Grid, List as ListIcon, Search, Trash2, Upload, } from "lucide-react";
|
|
13
|
+
import { useEffect, useState } from "react";
|
|
14
|
+
import { toast } from "sonner";
|
|
15
|
+
export default function MediaPage() {
|
|
16
|
+
const [items, setItems] = useState([]);
|
|
17
|
+
const [loading, setLoading] = useState(true);
|
|
18
|
+
const [uploading, setUploading] = useState(false);
|
|
19
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
20
|
+
const { t } = useTranslation();
|
|
21
|
+
const fetchMedia = async () => {
|
|
22
|
+
try {
|
|
23
|
+
setLoading(true);
|
|
24
|
+
const res = await fetch("/api/media");
|
|
25
|
+
const data = await res.json();
|
|
26
|
+
if (Array.isArray(data)) {
|
|
27
|
+
setItems(data);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (_e) {
|
|
31
|
+
toast.error("Failed to load media");
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
setLoading(false);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
fetchMedia();
|
|
39
|
+
}, []);
|
|
40
|
+
const handleUpload = async (e) => {
|
|
41
|
+
const file = e.target.files?.[0];
|
|
42
|
+
if (!file)
|
|
43
|
+
return;
|
|
44
|
+
const formData = new FormData();
|
|
45
|
+
formData.append("file", file);
|
|
46
|
+
try {
|
|
47
|
+
setUploading(true);
|
|
48
|
+
const res = await fetch("/api/media/upload", {
|
|
49
|
+
method: "POST",
|
|
50
|
+
body: formData,
|
|
51
|
+
});
|
|
52
|
+
if (res.ok) {
|
|
53
|
+
toast.success(t("File uploaded successfully"));
|
|
54
|
+
fetchMedia();
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
toast.error(t("Upload failed"));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (_e) {
|
|
61
|
+
toast.error(t("An error occurred during upload"));
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
setUploading(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const handleDelete = async (id) => {
|
|
68
|
+
if (!confirm(t("Are you sure you want to delete this file?")))
|
|
69
|
+
return;
|
|
70
|
+
try {
|
|
71
|
+
const res = await fetch(`/api/media/delete?id=${id}`, {
|
|
72
|
+
method: "DELETE",
|
|
73
|
+
});
|
|
74
|
+
if (res.ok) {
|
|
75
|
+
toast.success(t("File deleted successfully"));
|
|
76
|
+
setItems((prev) => prev.filter((item) => item.id !== id));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
toast.error(t("Delete failed"));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (_e) {
|
|
83
|
+
toast.error(t("An error occurred during deletion"));
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const filteredItems = items.filter((item) => item.filename.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
87
|
+
return (_jsxs("div", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold tracking-tight", children: t("Media Library") }), _jsx("p", { className: "text-muted-foreground", children: t("Manage your images and files.") })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "icon", children: _jsx(Grid, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "icon", children: _jsx(ListIcon, { className: "h-4 w-4" }) }), _jsxs("label", { className: "cursor-pointer", children: [_jsx("input", { type: "file", className: "hidden", onChange: handleUpload, disabled: uploading }), _jsx(Button, { asChild: true, disabled: uploading, children: _jsxs("span", { children: [_jsx(Upload, { className: "mr-2 h-4 w-4" }), uploading ? t("Uploading...") : t("Upload")] }) })] })] })] }), _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), _jsx(Input, { placeholder: t("Search files..."), className: "pl-10 max-w-sm", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value) })] }), loading ? (_jsx("div", { className: "grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4", children: Array.from({ length: 12 }).map((_, i) => (_jsx(Skeleton, { className: "aspect-square rounded-xl" }, i))) })) : filteredItems.length > 0 ? (_jsx("div", { className: "grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4", children: filteredItems.map((item) => (_jsxs(Card, { className: "group relative aspect-square overflow-hidden border-none bg-muted hover:ring-2 hover:ring-primary transition-all", children: [item.mimeType.startsWith("image/") ? (_jsx("img", { src: item.url, alt: item.filename, className: "h-full w-full object-cover transition-transform group-hover:scale-105" })) : (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsx("div", { className: "text-xs font-medium uppercase text-muted-foreground", children: item.mimeType.split("/")[1] }) })), _jsxs("div", { className: "absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex flex-col justify-end p-2 text-white", children: [_jsx("p", { className: "text-[10px] truncate font-medium", children: item.filename }), _jsxs("div", { className: "flex justify-between mt-1", children: [_jsx("button", { className: "hover:text-primary", onClick: (e) => {
|
|
88
|
+
e.stopPropagation();
|
|
89
|
+
window.open(item.url, "_blank");
|
|
90
|
+
}, children: _jsx(ExternalLink, { className: "h-3 w-3" }) }), _jsx("button", { className: "hover:text-destructive", onClick: (e) => {
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
handleDelete(item.id);
|
|
93
|
+
}, children: _jsx(Trash2, { className: "h-3 w-3" }) })] })] })] }, item.id))) })) : (_jsxs("div", { className: "flex flex-col items-center justify-center py-20 text-center", children: [_jsx("div", { className: "bg-muted p-4 rounded-full mb-4", children: _jsx(Upload, { className: "h-8 w-8 text-muted-foreground" }) }), _jsx("h3", { className: "text-lg font-semibold", children: t("No files found") }), _jsx("p", { className: "text-muted-foreground max-w-xs", children: t("Start by uploading your first file to the media library.") })] }))] }));
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=MediaPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaPage.js","sourceRoot":"","sources":["../../src/pages/MediaPage.tsx"],"names":[],"mappings":"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"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,gBAAgB,CAAC;AAGxB,eAAO,MAAM,aAAa,EAAE,sBAAsB,EAKjD,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,kBAAkB,EAsBzC,CAAC"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import MediaPage from "./pages/MediaPage";
|
|
2
|
+
export const privateRoutes = [
|
|
3
|
+
{
|
|
4
|
+
path: "/media",
|
|
5
|
+
component: MediaPage,
|
|
6
|
+
},
|
|
7
|
+
];
|
|
8
|
+
export const apiRoutes = [
|
|
9
|
+
{
|
|
10
|
+
path: "/api/media",
|
|
11
|
+
handler: async (request) => {
|
|
12
|
+
const { GET } = await import("./api/list");
|
|
13
|
+
return GET();
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
path: "/api/media/upload",
|
|
18
|
+
handler: async (request) => {
|
|
19
|
+
const { POST } = await import("./api/upload");
|
|
20
|
+
return POST(request);
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
path: "/api/media/delete",
|
|
25
|
+
handler: async (request) => {
|
|
26
|
+
const { DELETE: delHandler } = await import("./api/delete");
|
|
27
|
+
return delHandler(request);
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAIA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,MAAM,CAAC,MAAM,aAAa,GAA6B;IACrD;QACE,IAAI,EAAE,QAAQ;QACd,SAAS,EAAE,SAAS;KACrB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAyB;IAC7C;QACE,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,KAAK,EAAE,OAAgB,EAAE,EAAE;YAClC,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YAC3C,OAAO,GAAG,EAAE,CAAC;QACf,CAAC;KACF;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,KAAK,EAAE,OAAgB,EAAE,EAAE;YAClC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,KAAK,EAAE,OAAgB,EAAE,EAAE;YAClC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;YAC5D,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;KACF;CACF,CAAC"}
|