Skip to content

Commit

Permalink
Merge branch 'main' into bookmark-embeddings
Browse files Browse the repository at this point in the history
  • Loading branch information
MohamedBassem committed Jan 5, 2025
2 parents 01c74df + 0cea051 commit bee1d27
Show file tree
Hide file tree
Showing 136 changed files with 9,107 additions and 1,272 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ README.md
**/*.db
**/.env*
.git
./data
24 changes: 24 additions & 0 deletions .github/workflows/sdk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Publish CLI Package to npm
on:
push:
tags:
# This is a glob pattern not a regex
- 'sdk/v[0-9]+.[0-9]+.[0-9]+'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup
uses: ./tooling/github/setup

- name: Build SDK
run: pnpm build
working-directory: packages/sdk

- run: pnpm publish --access public --no-git-checks
working-directory: packages/sdk
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}

2 changes: 2 additions & 0 deletions apps/browser-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"dev": "vite",
"build": "tsc && vite build",
"format": "prettier .",
"format:fix": "prettier . --write",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --ext ts,tsx --fix",
"preview": "vite preview",
"typecheck": "tsc --noEmit"
},
Expand Down
2 changes: 1 addition & 1 deletion apps/browser-extension/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"jsx": "react-jsx",

"strict": true,
"noUnusedLocals": true,
"noUnusedLocals": false,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
Expand Down
2 changes: 2 additions & 0 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
"build": "vite build",
"run": "tsx src/index.ts",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier . --ignore-path ../../.prettierignore",
"format:fix": "prettier . --write --ignore-path ../../.prettierignore",
"typecheck": "tsc --noEmit"
},
"repository": {
Expand Down
10 changes: 7 additions & 3 deletions apps/cli/src/commands/bookmarks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ bookmarkCmd
collect<string>,
[],
)
.option(
"--title <title>",
"if set, this will be used as the bookmark's title",
)
.action(async (opts) => {
const api = getAPIClient();

Expand All @@ -81,15 +85,15 @@ bookmarkCmd
const promises = [
...opts.link.map((url) =>
api.bookmarks.createBookmark
.mutate({ type: BookmarkTypes.LINK, url })
.mutate({ type: BookmarkTypes.LINK, url, title: opts.title })
.then((bookmark: ZBookmark) => {
results.push(normalizeBookmark(bookmark));
})
.catch(printError(`Failed to add a link bookmark for url "${url}"`)),
),
...opts.note.map((text) =>
api.bookmarks.createBookmark
.mutate({ type: BookmarkTypes.TEXT, text })
.mutate({ type: BookmarkTypes.TEXT, text, title: opts.title })
.then((bookmark: ZBookmark) => {
results.push(normalizeBookmark(bookmark));
})
Expand All @@ -105,7 +109,7 @@ bookmarkCmd
const text = fs.readFileSync(0, "utf-8");
promises.push(
api.bookmarks.createBookmark
.mutate({ type: BookmarkTypes.TEXT, text })
.mutate({ type: BookmarkTypes.TEXT, text, title: opts.title })
.then((bookmark: ZBookmark) => {
results.push(normalizeBookmark(bookmark));
})
Expand Down
12 changes: 10 additions & 2 deletions apps/cli/src/commands/lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,17 @@ listsCmd
.action(async (opts) => {
const api = getAPIClient();
try {
const results = await api.lists.get.query({ listId: opts.list });
let resp = await api.bookmarks.getBookmarks.query({ listId: opts.list });
let results: string[] = resp.bookmarks.map((b) => b.id);
while (resp.nextCursor) {
resp = await api.bookmarks.getBookmarks.query({
listId: opts.list,
cursor: resp.nextCursor,
});
results = [...results, ...resp.bookmarks.map((b) => b.id)];
}

printObject(results.bookmarks);
printObject(results);
} catch (error) {
printErrorMessageWithReason(
"Failed to get the ids of the bookmarks in the list",
Expand Down
6 changes: 3 additions & 3 deletions apps/landing/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ function NavBar() {
href={GITHUB_LINK}
className="flex justify-center gap-2 text-center"
>
Github
GitHub
</Link>
<Link
href={DEMO_LINK}
Expand Down Expand Up @@ -162,7 +162,7 @@ function Hero() {
buttonVariants({ variant: "outline", size: "lg" }),
)}
>
<Github /> Github
<Github /> GitHub
</Link>
</div>
</div>
Expand Down Expand Up @@ -234,7 +234,7 @@ function Footer() {
href={GITHUB_LINK}
className="flex justify-center gap-2 text-center"
>
Github
GitHub
</Link>
</div>
</div>
Expand Down
6 changes: 4 additions & 2 deletions apps/landing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@
"start": "next start",
"lint": "next lint",
"typecheck": "tsc --noEmit",
"format": "prettier --check . --ignore-path ../../.gitignore"
"format": "prettier --check . --ignore-path ../../.gitignore",
"format:fix": "prettier --write . --ignore-path ../../.gitignore",
"lint:fix": "next lint --fix"
},
"dependencies": {
"@radix-ui/react-slot": "^1.0.2",
"@svgr/webpack": "^8.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"lucide-react": "^0.330.0",
"next": "14.2.15",
"next": "14.2.21",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-select": "^5.8.0",
Expand Down
19 changes: 16 additions & 3 deletions apps/mobile/components/bookmarks/BookmarkCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ActivityIndicator,
Alert,
Image,
Platform,
Pressable,
Expand Down Expand Up @@ -70,6 +71,20 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) {
onError,
});

const deleteBookmarkAlert = () =>
Alert.alert(
"Delete bookmark?",
"Are you sure you want to delete this bookmark?",
[
{ text: "Cancel", style: "cancel" },
{
text: "Delete",
onPress: () => deleteBookmark({ bookmarkId: bookmark.id }),
style: "destructive",
},
],
);

return (
<View className="flex flex-row gap-4">
{(isArchivePending || isDeletionPending) && <ActivityIndicator />}
Expand All @@ -93,9 +108,7 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) {
onPressAction={({ nativeEvent }) => {
Haptics.selectionAsync();
if (nativeEvent.event === "delete") {
deleteBookmark({
bookmarkId: bookmark.id,
});
deleteBookmarkAlert();
} else if (nativeEvent.event === "archive") {
archiveBookmark({
bookmarkId: bookmark.id,
Expand Down
2 changes: 2 additions & 0 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"ios": "expo run:ios",
"web": "expo start --web",
"format": "prettier .",
"format:fix": "prettier . --write",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"typecheck": "tsc --noEmit"
},
"dependencies": {
Expand Down
5 changes: 5 additions & 0 deletions apps/web/app/admin/actions/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import AdminActions from "@/components/admin/AdminActions";

export default function AdminActionsPage() {
return <AdminActions />;
}
61 changes: 61 additions & 0 deletions apps/web/app/admin/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { redirect } from "next/navigation";
import { AdminCard } from "@/components/admin/AdminCard";
import { AdminNotices } from "@/components/admin/AdminNotices";
import MobileSidebar from "@/components/shared/sidebar/MobileSidebar";
import Sidebar from "@/components/shared/sidebar/Sidebar";
import SidebarLayout from "@/components/shared/sidebar/SidebarLayout";
import { getServerAuthSession } from "@/server/auth";
import { TFunction } from "i18next";
import { Activity, ArrowLeft, Settings, Users } from "lucide-react";

const adminSidebarItems = (
t: TFunction,
): {
name: string;
icon: JSX.Element;
path: string;
}[] => [
{
name: t("settings.back_to_app"),
icon: <ArrowLeft size={18} />,
path: "/dashboard/bookmarks",
},
{
name: t("admin.server_stats.server_stats"),
icon: <Activity size={18} />,
path: "/admin/overview",
},
{
name: t("admin.users_list.users_list"),
icon: <Users size={18} />,
path: "/admin/users",
},
{
name: t("common.actions"),
icon: <Settings size={18} />,
path: "/admin/actions",
},
];

export default async function AdminLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const session = await getServerAuthSession();
if (!session || session.user.role !== "admin") {
redirect("/");
}

return (
<SidebarLayout
sidebar={<Sidebar items={adminSidebarItems} />}
mobileSidebar={<MobileSidebar items={adminSidebarItems} />}
>
<div className="flex flex-col gap-1">
<AdminNotices />
<AdminCard>{children}</AdminCard>
</div>
</SidebarLayout>
);
}
5 changes: 5 additions & 0 deletions apps/web/app/admin/overview/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ServerStats from "@/components/admin/ServerStats";

export default function AdminOverviewPage() {
return <ServerStats />;
}
6 changes: 6 additions & 0 deletions apps/web/app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { redirect } from "next/navigation";

export default function AdminHomepage() {
redirect("/admin/overview");
return null;
}
5 changes: 5 additions & 0 deletions apps/web/app/admin/users/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import UserList from "@/components/admin/UserList";

export default function AdminUsersPage() {
return <UserList />;
}
75 changes: 52 additions & 23 deletions apps/web/app/api/assets/[assetId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { createContextFromRequest } from "@/server/api/client";
import { and, eq } from "drizzle-orm";

import { assets } from "@hoarder/db/schema";
import { readAsset } from "@hoarder/shared/assetdb";
import {
createAssetReadStream,
getAssetSize,
readAssetMetadata,
} from "@hoarder/shared/assetdb";

export const dynamic = "force-dynamic";
export async function GET(
Expand All @@ -22,35 +26,60 @@ export async function GET(
return Response.json({ error: "Asset not found" }, { status: 404 });
}

const { asset, metadata } = await readAsset({
userId: ctx.user.id,
assetId: params.assetId,
});
const [metadata, size] = await Promise.all([
readAssetMetadata({
userId: ctx.user.id,
assetId: params.assetId,
}),

getAssetSize({
userId: ctx.user.id,
assetId: params.assetId,
}),
]);

const range = request.headers.get("Range");
if (range) {
const parts = range.replace(/bytes=/, "").split("-");
const start = parseInt(parts[0], 10);
const end = parts[1] ? parseInt(parts[1], 10) : asset.length - 1;

// TODO: Don't read the whole asset into memory in the first place
const chunk = asset.subarray(start, end + 1);
return new Response(chunk, {
status: 206, // Partial Content
headers: {
"Content-Range": `bytes ${start}-${end}/${asset.length}`,
"Accept-Ranges": "bytes",
"Content-Length": chunk.length.toString(),
"Content-type": metadata.contentType,
},
const end = parts[1] ? parseInt(parts[1], 10) : size - 1;

const stream = createAssetReadStream({
userId: ctx.user.id,
assetId: params.assetId,
start,
end,
});
} else {
return new Response(asset, {
status: 200,
headers: {
"Content-Length": asset.length.toString(),
"Content-type": metadata.contentType,

return new Response(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
stream as any,
{
status: 206, // Partial Content
headers: {
"Content-Range": `bytes ${start}-${end}/${size}`,
"Accept-Ranges": "bytes",
"Content-Length": (end - start + 1).toString(),
"Content-type": metadata.contentType,
},
},
);
} else {
const stream = createAssetReadStream({
userId: ctx.user.id,
assetId: params.assetId,
});

return new Response(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
stream as any,
{
status: 200,
headers: {
"Content-Length": size.toString(),
"Content-type": metadata.contentType,
},
},
);
}
}
Loading

0 comments on commit bee1d27

Please sign in to comment.