aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/web/components/dashboard/preview/AttachmentBox.tsx85
-rw-r--r--apps/web/lib/attachments.tsx2
-rw-r--r--packages/db/schema.ts2
-rw-r--r--packages/open-api/karakeep-openapi-spec.json11
-rw-r--r--packages/open-api/lib/bookmarks.ts6
-rw-r--r--packages/shared/types/bookmarks.ts2
-rw-r--r--packages/trpc/lib/attachments.ts5
-rw-r--r--packages/trpc/models/bookmarks.ts1
-rw-r--r--packages/trpc/routers/assets.ts17
-rw-r--r--packages/trpc/routers/bookmarks.ts1
10 files changed, 101 insertions, 31 deletions
diff --git a/apps/web/components/dashboard/preview/AttachmentBox.tsx b/apps/web/components/dashboard/preview/AttachmentBox.tsx
index 87c56eb8..e24cc646 100644
--- a/apps/web/components/dashboard/preview/AttachmentBox.tsx
+++ b/apps/web/components/dashboard/preview/AttachmentBox.tsx
@@ -101,7 +101,11 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) {
prefetch={false}
>
{ASSET_TYPE_TO_ICON[asset.assetType]}
- <p>{humanFriendlyNameForAssertType(asset.assetType)}</p>
+ <p>
+ {asset.assetType === "userUploaded" && asset.fileName
+ ? asset.fileName
+ : humanFriendlyNameForAssertType(asset.assetType)}
+ </p>
</Link>
<div className="flex gap-2">
<Link
@@ -109,35 +113,40 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) {
target="_blank"
href={getAssetUrl(asset.id)}
className="flex items-center gap-1"
- download={humanFriendlyNameForAssertType(asset.assetType)}
+ download={
+ asset.assetType === "userUploaded" && asset.fileName
+ ? asset.fileName
+ : humanFriendlyNameForAssertType(asset.assetType)
+ }
prefetch={false}
>
<Download className="size-4" />
</Link>
- {isAllowedToAttachAsset(asset.assetType) && (
- <FilePickerButton
- title="Replace"
- loading={isReplacing}
- accept=".jgp,.JPG,.jpeg,.png,.webp"
- multiple={false}
- variant="none"
- size="none"
- className="flex items-center gap-2"
- onFileSelect={(file) =>
- uploadAsset(file, {
- onSuccess: (resp) => {
- replaceAsset({
- bookmarkId: bookmark.id,
- oldAssetId: asset.id,
- newAssetId: resp.assetId,
- });
- },
- })
- }
- >
- <Pencil className="size-4" />
- </FilePickerButton>
- )}
+ {isAllowedToAttachAsset(asset.assetType) &&
+ asset.assetType !== "userUploaded" && (
+ <FilePickerButton
+ title="Replace"
+ loading={isReplacing}
+ accept=".jgp,.JPG,.jpeg,.png,.webp"
+ multiple={false}
+ variant="none"
+ size="none"
+ className="flex items-center gap-2"
+ onFileSelect={(file) =>
+ uploadAsset(file, {
+ onSuccess: (resp) => {
+ replaceAsset({
+ bookmarkId: bookmark.id,
+ oldAssetId: asset.id,
+ newAssetId: resp.assetId,
+ });
+ },
+ })
+ }
+ >
+ <Pencil className="size-4" />
+ </FilePickerButton>
+ )}
{isAllowedToDetachAsset(asset.assetType) && (
<ActionConfirmingDialog
title="Delete Attachment?"
@@ -194,6 +203,30 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) {
Attach a Banner
</FilePickerButton>
)}
+ <FilePickerButton
+ title="Upload File"
+ loading={isAttaching}
+ multiple={false}
+ variant="ghost"
+ size="none"
+ className="flex w-full items-center justify-center gap-2"
+ onFileSelect={(file) =>
+ uploadAsset(file, {
+ onSuccess: (resp) => {
+ attachAsset({
+ bookmarkId: bookmark.id,
+ asset: {
+ id: resp.assetId,
+ assetType: "userUploaded",
+ },
+ });
+ },
+ })
+ }
+ >
+ <Plus className="size-4" />
+ Upload File
+ </FilePickerButton>
</CollapsibleContent>
</Collapsible>
);
diff --git a/apps/web/lib/attachments.tsx b/apps/web/lib/attachments.tsx
index ce34b295..67941098 100644
--- a/apps/web/lib/attachments.tsx
+++ b/apps/web/lib/attachments.tsx
@@ -4,6 +4,7 @@ import {
FileCode,
Image,
Paperclip,
+ Upload,
Video,
} from "lucide-react";
@@ -18,5 +19,6 @@ export const ASSET_TYPE_TO_ICON: Record<ZAssetType, React.ReactNode> = {
video: <Video className="size-4" />,
bookmarkAsset: <Paperclip className="size-4" />,
linkHtmlContent: <FileCode className="size-4" />,
+ userUploaded: <Upload className="size-4" />,
unknown: <Paperclip className="size-4" />,
};
diff --git a/packages/db/schema.ts b/packages/db/schema.ts
index c0433d6a..5f523d21 100644
--- a/packages/db/schema.ts
+++ b/packages/db/schema.ts
@@ -228,6 +228,7 @@ export const enum AssetTypes {
LINK_VIDEO = "linkVideo",
LINK_HTML_CONTENT = "linkHtmlContent",
BOOKMARK_ASSET = "bookmarkAsset",
+ USER_UPLOADED = "userUploaded",
UNKNOWN = "unknown",
}
@@ -246,6 +247,7 @@ export const assets = sqliteTable(
AssetTypes.LINK_VIDEO,
AssetTypes.LINK_HTML_CONTENT,
AssetTypes.BOOKMARK_ASSET,
+ AssetTypes.USER_UPLOADED,
AssetTypes.UNKNOWN,
],
}).notNull(),
diff --git a/packages/open-api/karakeep-openapi-spec.json b/packages/open-api/karakeep-openapi-spec.json
index 0b520b00..2e791fbf 100644
--- a/packages/open-api/karakeep-openapi-spec.json
+++ b/packages/open-api/karakeep-openapi-spec.json
@@ -317,8 +317,13 @@
"video",
"bookmarkAsset",
"precrawledArchive",
+ "userUploaded",
"unknown"
]
+ },
+ "fileName": {
+ "type": "string",
+ "nullable": true
}
},
"required": [
@@ -1703,6 +1708,7 @@
"video",
"bookmarkAsset",
"precrawledArchive",
+ "userUploaded",
"unknown"
]
}
@@ -1737,8 +1743,13 @@
"video",
"bookmarkAsset",
"precrawledArchive",
+ "userUploaded",
"unknown"
]
+ },
+ "fileName": {
+ "type": "string",
+ "nullable": true
}
},
"required": [
diff --git a/packages/open-api/lib/bookmarks.ts b/packages/open-api/lib/bookmarks.ts
index d49a20ff..0a3806b9 100644
--- a/packages/open-api/lib/bookmarks.ts
+++ b/packages/open-api/lib/bookmarks.ts
@@ -6,6 +6,7 @@ import { z } from "zod";
import {
zAssetSchema,
+ zAssetTypesSchema,
zBareBookmarkSchema,
zManipulatedTagSchema,
zNewBookmarkRequestSchema,
@@ -418,7 +419,10 @@ registry.registerPath({
description: "The asset to attach",
content: {
"application/json": {
- schema: zAssetSchema,
+ schema: z.object({
+ id: z.string(),
+ assetType: zAssetTypesSchema,
+ }),
},
},
},
diff --git a/packages/shared/types/bookmarks.ts b/packages/shared/types/bookmarks.ts
index f108d006..d72525d1 100644
--- a/packages/shared/types/bookmarks.ts
+++ b/packages/shared/types/bookmarks.ts
@@ -24,6 +24,7 @@ export const zAssetTypesSchema = z.enum([
"video",
"bookmarkAsset",
"precrawledArchive",
+ "userUploaded",
"unknown",
]);
export type ZAssetType = z.infer<typeof zAssetTypesSchema>;
@@ -31,6 +32,7 @@ export type ZAssetType = z.infer<typeof zAssetTypesSchema>;
export const zAssetSchema = z.object({
id: z.string(),
assetType: zAssetTypesSchema,
+ fileName: z.string().nullish(),
});
export const zBookmarkedLinkSchema = z.object({
diff --git a/packages/trpc/lib/attachments.ts b/packages/trpc/lib/attachments.ts
index af484462..7a4e2668 100644
--- a/packages/trpc/lib/attachments.ts
+++ b/packages/trpc/lib/attachments.ts
@@ -16,6 +16,7 @@ export function mapDBAssetTypeToUserType(assetType: AssetTypes): ZAssetType {
[AssetTypes.LINK_VIDEO]: "video",
[AssetTypes.LINK_HTML_CONTENT]: "linkHtmlContent",
[AssetTypes.BOOKMARK_ASSET]: "bookmarkAsset",
+ [AssetTypes.USER_UPLOADED]: "userUploaded",
[AssetTypes.UNKNOWN]: "bannerImage",
};
return map[assetType];
@@ -33,6 +34,7 @@ export function mapSchemaAssetTypeToDB(
video: AssetTypes.LINK_VIDEO,
bookmarkAsset: AssetTypes.BOOKMARK_ASSET,
linkHtmlContent: AssetTypes.LINK_HTML_CONTENT,
+ userUploaded: AssetTypes.USER_UPLOADED,
unknown: AssetTypes.UNKNOWN,
};
return map[assetType];
@@ -48,6 +50,7 @@ export function humanFriendlyNameForAssertType(type: ZAssetType) {
video: "Video",
bookmarkAsset: "Bookmark Asset",
linkHtmlContent: "HTML Content",
+ userUploaded: "User Uploaded File",
unknown: "Unknown",
};
return map[type];
@@ -63,6 +66,7 @@ export function isAllowedToAttachAsset(type: ZAssetType) {
video: true,
bookmarkAsset: false,
linkHtmlContent: false,
+ userUploaded: true,
unknown: false,
};
return map[type];
@@ -78,6 +82,7 @@ export function isAllowedToDetachAsset(type: ZAssetType) {
video: true,
bookmarkAsset: false,
linkHtmlContent: false,
+ userUploaded: true,
unknown: false,
};
return map[type];
diff --git a/packages/trpc/models/bookmarks.ts b/packages/trpc/models/bookmarks.ts
index e0864ec1..c689f64d 100644
--- a/packages/trpc/models/bookmarks.ts
+++ b/packages/trpc/models/bookmarks.ts
@@ -295,6 +295,7 @@ export class Bookmark implements PrivacyAware {
acc[bookmarkId].assets.push({
id: row.assets.id,
assetType: mapDBAssetTypeToUserType(row.assets.assetType),
+ fileName: row.assets.fileName,
});
}
diff --git a/packages/trpc/routers/assets.ts b/packages/trpc/routers/assets.ts
index af508a4e..7be85446 100644
--- a/packages/trpc/routers/assets.ts
+++ b/packages/trpc/routers/assets.ts
@@ -99,7 +99,10 @@ export const assetsAppRouter = router({
.input(
z.object({
bookmarkId: z.string(),
- asset: zAssetSchema,
+ asset: z.object({
+ id: z.string(),
+ assetType: zAssetTypesSchema,
+ }),
}),
)
.output(zAssetSchema)
@@ -112,7 +115,7 @@ export const assetsAppRouter = router({
message: "You can't attach this type of asset",
});
}
- await ctx.db
+ const [updatedAsset] = await ctx.db
.update(assets)
.set({
assetType: mapSchemaAssetTypeToDB(input.asset.assetType),
@@ -120,8 +123,14 @@ export const assetsAppRouter = router({
})
.where(
and(eq(assets.id, input.asset.id), eq(assets.userId, ctx.user.id)),
- );
- return input.asset;
+ )
+ .returning();
+
+ return {
+ id: updatedAsset.id,
+ assetType: mapDBAssetTypeToUserType(updatedAsset.assetType),
+ fileName: updatedAsset.fileName,
+ };
}),
replaceAsset: authedProcedure
.input(
diff --git a/packages/trpc/routers/bookmarks.ts b/packages/trpc/routers/bookmarks.ts
index 5a2fc1eb..72c6c1d1 100644
--- a/packages/trpc/routers/bookmarks.ts
+++ b/packages/trpc/routers/bookmarks.ts
@@ -253,6 +253,7 @@ async function toZodSchema(
assets: assets.map((a) => ({
id: a.id,
assetType: mapDBAssetTypeToUserType(a.assetType),
+ fileName: a.fileName,
})),
...rest,
};