mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-07 02:58:11 +00:00
feat: add loading card for memories page
This commit is contained in:
parent
1dafbc691b
commit
e05c644143
23 changed files with 1485 additions and 2218 deletions
|
|
@ -25,7 +25,7 @@ import chunkText from "./queueConsumer/chunkers/chonker";
|
|||
import { systemPrompt, template } from "./prompts/prompt1";
|
||||
import { swaggerUI } from "@hono/swagger-ui";
|
||||
import { database } from "./db";
|
||||
import { storedContent } from "@repo/db/schema";
|
||||
import { jobs, storedContent, users } from "@repo/db/schema";
|
||||
import { sql, and, eq } from "drizzle-orm";
|
||||
import { LIMITS } from "@repo/shared-types";
|
||||
import { typeDecider } from "./queueConsumer/utils/typeDecider";
|
||||
|
|
@ -34,8 +34,8 @@ import {
|
|||
chunkNote,
|
||||
chunkPage,
|
||||
} from "./queueConsumer/chunkers/chunkPageOrNotes";
|
||||
import { queue } from "./queueConsumer";
|
||||
import { isErr } from "./errors/results";
|
||||
import { d1ErrorFactory, queue } from "./queueConsumer";
|
||||
import { isErr, wrap } from "./errors/results";
|
||||
import { createOpenAI } from "@ai-sdk/openai";
|
||||
|
||||
const app = new Hono<{ Bindings: Env }>();
|
||||
|
|
@ -119,11 +119,33 @@ app.post("/api/add", zValidator("json", vectorBody), async (c) => {
|
|||
// unique contraint check
|
||||
|
||||
if (isWithinLimit) {
|
||||
//insert to the jobs table
|
||||
const job = await wrap(
|
||||
db
|
||||
.insert(jobs)
|
||||
.values({
|
||||
userId: body.user,
|
||||
url: saveToDbUrl,
|
||||
status: "Processing",
|
||||
attempts: 0,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.returning({ jobId: jobs.id }),
|
||||
d1ErrorFactory,
|
||||
"Error When inserting into jobs table",
|
||||
);
|
||||
if (isErr(job)) {
|
||||
throw job.error;
|
||||
}
|
||||
|
||||
const jobId = job.value[0].jobId;
|
||||
await c.env.EMBEDCHUNKS_QUEUE.send({
|
||||
content: saveToDbUrl,
|
||||
user: body.user,
|
||||
space: body.spaces,
|
||||
type: type,
|
||||
jobId: jobId,
|
||||
});
|
||||
} else {
|
||||
return c.json({
|
||||
|
|
@ -775,6 +797,31 @@ app.get("/howFuckedAreWe", async (c) => {
|
|||
return c.json({ fuckedPercent });
|
||||
});
|
||||
|
||||
app.get(
|
||||
"api/memories",
|
||||
zValidator(
|
||||
"query",
|
||||
z.object({
|
||||
userId: z.string(),
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
const { userId } = c.req.valid("query");
|
||||
const db = database(c.env);
|
||||
const spaces = await db.query.space.findMany({
|
||||
where: eq(users, userId),
|
||||
});
|
||||
|
||||
const memories = await db.query.storedContent.findMany({
|
||||
where: eq(users, userId),
|
||||
});
|
||||
const processingJobs = await db.query.jobs.findMany({
|
||||
where: and(eq(jobs.userId, userId), eq(jobs.status, "Processing")),
|
||||
});
|
||||
return c.json({ spaces, memories, processingJobs });
|
||||
},
|
||||
);
|
||||
|
||||
export default {
|
||||
fetch: app.fetch,
|
||||
queue,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ export function chunkThread(threadText: string): TweetChunks {
|
|||
if (typeof threadText == "string") {
|
||||
console.log("DA WORKER FAILED DO SOMEHTING FIX DA WROKER", thread);
|
||||
const rawTweet = getRawTweet(thread);
|
||||
console.log(rawTweet);
|
||||
const parsedTweet: any = JSON.parse(rawTweet);
|
||||
|
||||
const chunkedTweet = chunkText(parsedTweet.text, 1536);
|
||||
|
|
@ -56,7 +55,6 @@ export function chunkThread(threadText: string): TweetChunks {
|
|||
|
||||
return { type: "tweet", chunks };
|
||||
} else {
|
||||
console.log("thread in else statement", JSON.stringify(thread));
|
||||
const chunkedTweets = (thread as any).map((tweet: Tweet) => {
|
||||
const chunkedTweet = chunkText(tweet.text, 1536);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ export async function processPage(input: {
|
|||
throw metadataResult.error;
|
||||
}
|
||||
const metadata = metadataResult.value;
|
||||
console.log("[this is the metadata]", metadata);
|
||||
return Ok({ pageContent, metadata });
|
||||
} catch (e) {
|
||||
console.error("[Page Processing Error]", e);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ export const getTweetData = async (
|
|||
tweetID: string,
|
||||
): Promise<Result<GetTweetResult, ProcessTweetError>> => {
|
||||
try {
|
||||
console.log("is fetch defined here?");
|
||||
const url = `https://cdn.syndication.twimg.com/tweet-result?id=${tweetID}&lang=en&features=tfw_timeline_list%3A%3Btfw_follower_count_sunset%3Atrue%3Btfw_tweet_edit_backend%3Aon%3Btfw_refsrc_session%3Aon%3Btfw_fosnr_soft_interventions_enabled%3Aon%3Btfw_show_birdwatch_pivots_enabled%3Aon%3Btfw_show_business_verified_badge%3Aon%3Btfw_duplicate_scribes_to_settings%3Aon%3Btfw_use_profile_image_shape_enabled%3Aon%3Btfw_show_blue_verified_badge%3Aon%3Btfw_legacy_timeline_sunset%3Atrue%3Btfw_show_gov_verified_badge%3Aon%3Btfw_show_business_affiliate_badge%3Aon%3Btfw_tweet_edit_frontend%3Aon&token=4c2mmul6mnh`;
|
||||
|
||||
const resp = await fetch(url, {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class D1InsertError extends BaseError {
|
|||
}
|
||||
}
|
||||
|
||||
const d1ErrorFactory = (err: Error, source: string) =>
|
||||
export const d1ErrorFactory = (err: Error, source: string) =>
|
||||
new D1InsertError(err.message, source);
|
||||
|
||||
const calculateExponentialBackoff = (
|
||||
|
|
@ -52,11 +52,12 @@ export async function queue(
|
|||
space: Array<number>;
|
||||
user: string;
|
||||
type: string;
|
||||
jobId: number;
|
||||
}>,
|
||||
env: Env,
|
||||
): Promise<void> {
|
||||
const db = database(env);
|
||||
|
||||
console.log("[This is the batch]", JSON.stringify(batch));
|
||||
for (let message of batch.messages) {
|
||||
const body = message.body;
|
||||
|
||||
|
|
@ -71,63 +72,38 @@ export async function queue(
|
|||
throw userExists.error;
|
||||
}
|
||||
|
||||
//check if this is a retry job.. by checking if the combination of the userId and the url already exists on the queue
|
||||
let jobId;
|
||||
const existingJob = await wrap(
|
||||
db
|
||||
.select()
|
||||
.from(jobs)
|
||||
.where(
|
||||
and(
|
||||
eq(jobs.userId, userExists.value[0].id),
|
||||
eq(jobs.url, body.content),
|
||||
),
|
||||
)
|
||||
.limit(1),
|
||||
//take the job id from the body and update the job table with attempts + 1
|
||||
const jobId = body.jobId;
|
||||
|
||||
const jobResult = await wrap(
|
||||
db.select().from(jobs).where(eq(jobs.id, jobId)).limit(1),
|
||||
d1ErrorFactory,
|
||||
"Error when checking for existing job",
|
||||
"Error when fetching job",
|
||||
);
|
||||
|
||||
if (isErr(existingJob)) {
|
||||
throw existingJob.error;
|
||||
if (isErr(jobResult)) {
|
||||
throw jobResult.error;
|
||||
}
|
||||
|
||||
if (existingJob.value.length > 0) {
|
||||
jobId = existingJob.value[0].id;
|
||||
await wrap(
|
||||
db
|
||||
.update(jobs)
|
||||
.set({
|
||||
attempts: existingJob.value[0].attempts + 1,
|
||||
updatedAt: new Date(),
|
||||
status: "Processing",
|
||||
})
|
||||
.where(eq(jobs.id, jobId)),
|
||||
d1ErrorFactory,
|
||||
"Error when updating job attempts",
|
||||
);
|
||||
} else {
|
||||
const job = await wrap(
|
||||
db
|
||||
.insert(jobs)
|
||||
.values({
|
||||
userId: userExists.value[0].id as string,
|
||||
url: body.content,
|
||||
status: "Processing",
|
||||
attempts: 1,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.returning({ jobId: jobs.id }),
|
||||
d1ErrorFactory,
|
||||
"Error When inserting into jobs table",
|
||||
);
|
||||
if (isErr(job)) {
|
||||
throw job.error;
|
||||
}
|
||||
jobId = job.value[0].jobId;
|
||||
const job = jobResult.value[0];
|
||||
|
||||
if (!job) {
|
||||
throw new Error("Job not found");
|
||||
}
|
||||
|
||||
await wrap(
|
||||
db
|
||||
.update(jobs)
|
||||
.set({
|
||||
attempts: job.attempts + 1,
|
||||
updatedAt: new Date(),
|
||||
status: "Processing",
|
||||
})
|
||||
.where(eq(jobs.id, jobId)),
|
||||
d1ErrorFactory,
|
||||
"Error when updating job attempts",
|
||||
);
|
||||
|
||||
let pageContent: string;
|
||||
let vectorData: string;
|
||||
let metadata: Metadata;
|
||||
|
|
@ -136,7 +112,6 @@ export async function queue(
|
|||
let noteId = 0;
|
||||
switch (type) {
|
||||
case "note": {
|
||||
console.log("note hit");
|
||||
const note = processNote(body.content);
|
||||
if (isErr(note)) {
|
||||
throw note.error;
|
||||
|
|
@ -149,13 +124,11 @@ export async function queue(
|
|||
break;
|
||||
}
|
||||
case "page": {
|
||||
console.log("page hit");
|
||||
const page = await processPage({
|
||||
url: body.content,
|
||||
securityKey: env.MD_SEC_KEY,
|
||||
});
|
||||
if (isErr(page)) {
|
||||
console.log("there is a page error here");
|
||||
throw page.error;
|
||||
}
|
||||
pageContent = page.value.pageContent;
|
||||
|
|
@ -172,7 +145,7 @@ export async function queue(
|
|||
tweetUrl: body.content,
|
||||
env: env,
|
||||
});
|
||||
console.log("[This is the thread]", thread);
|
||||
|
||||
if (isErr(tweet)) {
|
||||
throw tweet.error;
|
||||
}
|
||||
|
|
@ -368,9 +341,8 @@ export async function queue(
|
|||
.update(jobs)
|
||||
.set({ status: "Processed" })
|
||||
.where(eq(jobs.id, jobId));
|
||||
|
||||
return;
|
||||
}
|
||||
console.log(`Finished processing batch of ${batch.messages.length} messages`);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export interface JobData {
|
|||
space: Array<number>;
|
||||
user: string;
|
||||
type: string;
|
||||
jobId: number;
|
||||
}
|
||||
|
||||
export interface TweetData {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { Content, StoredSpace } from "@repo/db/schema";
|
||||
import { Content, Job, jobs, StoredSpace } from "@repo/db/schema";
|
||||
import { MemoriesIcon, NextIcon, UrlIcon } from "@repo/ui/icons";
|
||||
import {
|
||||
ArrowLeftIcon,
|
||||
|
|
@ -12,10 +12,11 @@ import {
|
|||
} from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import Masonry from "react-layout-masonry";
|
||||
import { getRawTweet } from "@repo/shared-types/utils";
|
||||
import { MyTweet } from "../../../components/twitter/render-tweet";
|
||||
import LoadingCard from "@/components/loadingCard/loadingCard";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -53,14 +54,25 @@ import {
|
|||
DocumentTextIcon,
|
||||
FolderIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { db } from "@/server/db";
|
||||
import { and, count, eq } from "drizzle-orm";
|
||||
import { usePendingJob } from "../../../contexts/PendingJobContext";
|
||||
|
||||
type TMemoriesPage = {
|
||||
memoriesAndSpaces: { memories: Content[]; spaces: StoredSpace[] };
|
||||
memoriesAndSpaces: {
|
||||
memories: Content[];
|
||||
spaces: StoredSpace[];
|
||||
userId: string;
|
||||
};
|
||||
title?: string;
|
||||
currentSpace?: StoredSpace;
|
||||
usersWithAccess?: string[];
|
||||
};
|
||||
|
||||
interface JobCountResponse {
|
||||
pendingJobs: number;
|
||||
}
|
||||
|
||||
export function MemoriesPage({
|
||||
memoriesAndSpaces,
|
||||
title = "Your Memories",
|
||||
|
|
@ -78,8 +90,65 @@ export function MemoriesPage({
|
|||
return "All";
|
||||
}, [tab]);
|
||||
|
||||
const userId = memoriesAndSpaces.userId;
|
||||
const [filter, setFilter] = useState(initialFilter);
|
||||
const [spaces, setSpaces] = useState<StoredSpace[]>(memoriesAndSpaces.spaces);
|
||||
const [newMemories, setNewMemories] = useState<Content[]>([]);
|
||||
const [lastFetchTimestamp, setLastFetchTimestamp] = useState<string>(
|
||||
new Date().toISOString(),
|
||||
);
|
||||
|
||||
const { pendingJobs, setPendingJobs } = usePendingJob();
|
||||
const fetchNewMemories = async (lastFetchTimestamp: string) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
"/api/getNewMemories?lastFetchTimestamp=" + lastFetchTimestamp,
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch new memories");
|
||||
}
|
||||
const data = (await response.json()) as { newMemories: Content[] };
|
||||
return data.newMemories;
|
||||
} catch (error) {
|
||||
console.error("Error fetching new memories:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchJobCount = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/getJobCount");
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch job count");
|
||||
}
|
||||
const data: JobCountResponse = await response.json();
|
||||
const newPendingJobs = data.pendingJobs;
|
||||
|
||||
// If pendingJobs has decreased, fetch new memories with the lastFetchTimestamp
|
||||
if (newPendingJobs < pendingJobs) {
|
||||
const newMemories = await fetchNewMemories(lastFetchTimestamp);
|
||||
if (newMemories) {
|
||||
setNewMemories((prev) => [...prev, ...newMemories]);
|
||||
setLastFetchTimestamp(new Date().toISOString());
|
||||
}
|
||||
}
|
||||
|
||||
setPendingJobs(newPendingJobs);
|
||||
} catch (error) {
|
||||
console.error("Error fetching job count:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchJobCount(); // Initial fetch
|
||||
const intervalId = setInterval(fetchJobCount, 5000); // Poll every 5 seconds
|
||||
|
||||
return () => clearInterval(intervalId); // Clean up on unmount
|
||||
}, [
|
||||
pendingJobs,
|
||||
memoriesAndSpaces.memories,
|
||||
setPendingJobs,
|
||||
lastFetchTimestamp,
|
||||
]);
|
||||
|
||||
const handleDeleteSpace = async (id: number) => {
|
||||
const response = await deleteSpace(id);
|
||||
|
|
@ -102,7 +171,6 @@ export function MemoriesPage({
|
|||
const json = JSON.stringify(dataToExport, null, 2);
|
||||
const blob = new Blob([json], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "memories_and_spaces.json";
|
||||
|
|
@ -115,6 +183,11 @@ export function MemoriesPage({
|
|||
const sortedItems = useMemo(() => {
|
||||
// merge spaces & memories to { item: "memory" | "space", date: Date, data: Content | StoredSpace }
|
||||
const unifiedItems = [
|
||||
...newMemories.map((memory) => ({
|
||||
item: "memory",
|
||||
date: new Date(memory.savedAt),
|
||||
data: memory,
|
||||
})),
|
||||
...memoriesAndSpaces.memories.map((memory) => ({
|
||||
item: "memory",
|
||||
date: new Date(memory.savedAt),
|
||||
|
|
@ -152,18 +225,19 @@ export function MemoriesPage({
|
|||
return false;
|
||||
})
|
||||
.sort((a, b) => b.date - a.date);
|
||||
}, [memoriesAndSpaces.memories, spaces, filter]);
|
||||
}, [memoriesAndSpaces.memories, spaces, filter, newMemories]);
|
||||
|
||||
return (
|
||||
<div className="px-2 md:px-32 py-36 h-full flex mx-auto w-full flex-col gap-6">
|
||||
<div className="space-y-2">
|
||||
{currentSpace && (
|
||||
<Link href={"/memories"}>
|
||||
<Button className="px-0 text-gray-300" variant="link">
|
||||
<Button className="px-0 text-gray-300" variant="link">
|
||||
<ArrowLeftIcon className="w-3 h-3" /> Back to all memories
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<h2 className="text-white w-full text-3xl text-left font-semibold">
|
||||
{title}
|
||||
</h2>
|
||||
|
|
@ -177,6 +251,7 @@ export function MemoriesPage({
|
|||
currentSpace ? SpaceFilterMethods : MemoriesFilterMethods
|
||||
}
|
||||
/>
|
||||
|
||||
<button
|
||||
onClick={handleExport}
|
||||
className={`transition px-4 py-2 rounded-lg text-[#B3BCC5] bg-secondary hover:bg-secondary hover:text-[#76a3cc]`}
|
||||
|
|
@ -198,6 +273,7 @@ export function MemoriesPage({
|
|||
alt="Spaces icon"
|
||||
className="w-3 h-3"
|
||||
/>
|
||||
|
||||
<span className="text-[#fff]">{user}</span>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -211,6 +287,7 @@ export function MemoriesPage({
|
|||
|
||||
if (!email) {
|
||||
toast.error("Please enter an email");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -229,6 +306,7 @@ export function MemoriesPage({
|
|||
name="email"
|
||||
placeholder="Add user by email"
|
||||
/>
|
||||
|
||||
<Button variant="secondary">Add</Button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -242,10 +320,17 @@ export function MemoriesPage({
|
|||
className: "min-w-[calc(33.3333%-16px)] w-full",
|
||||
}}
|
||||
>
|
||||
{pendingJobs > 0 && (
|
||||
<div className="w-full">
|
||||
<LoadingCard pendingJobs={pendingJobs} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{sortedItems.map((item) => {
|
||||
if (item.item === "memory") {
|
||||
return (
|
||||
<MemoryComponent
|
||||
key={item.data.id}
|
||||
type={(item.data as Content).type ?? "note"}
|
||||
content={(item.data as Content).content}
|
||||
title={(item.data as Content).title ?? "Untitled"}
|
||||
|
|
@ -267,6 +352,7 @@ export function MemoriesPage({
|
|||
if (item.item === "space") {
|
||||
return (
|
||||
<SpaceComponent
|
||||
key={item.data.id}
|
||||
title={(item.data as StoredSpace).name}
|
||||
description={`${(item.data as StoredSpace).numItems} memories`}
|
||||
id={(item.data as StoredSpace).id}
|
||||
|
|
@ -318,7 +404,7 @@ function SpaceComponent({
|
|||
<Image src={NextIcon} alt="Search icon" />
|
||||
</div>
|
||||
</Link>
|
||||
<div className="absolute z-40 right-3 top-3 opacity-0 group-hover:opacity-100 hover:text-red-600">
|
||||
<div className="absolute z-40 right-5 top-4 opacity-0 group-hover:opacity-100 hover:text-red-600">
|
||||
<SpaceDeleteAlert
|
||||
onClick={() => {
|
||||
handleDeleteSpace(id);
|
||||
|
|
@ -446,7 +532,7 @@ function MemoryComponent({
|
|||
}
|
||||
|
||||
const MemoriesFilterMethods = ["All", "Spaces", "Pages", "Notes", "Tweet"];
|
||||
const SpaceFilterMethods = ["All", "Pages", "Notes", "Tweet"];
|
||||
const SpaceFilterMethods = ["All", "Pages", "Notes", "Tweet", "Jobs"];
|
||||
function Filters({
|
||||
setFilter,
|
||||
filter,
|
||||
|
|
@ -463,7 +549,7 @@ function Filters({
|
|||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setFilter(i)}
|
||||
className={`transition px-4 py-2 ${i === filter ? " text-[#369DFD]" : "text-[#B3BCC5]"}`}
|
||||
className={`transition px-4 py-2 ${i === filter ? " text-[#369DFD]" : "text-[#B3BCC5]"}`}
|
||||
>
|
||||
{i}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ async function Page({ params: { spaceid } }: { params: { spaceid: number } }) {
|
|||
|
||||
return (
|
||||
<MemoriesPage
|
||||
memoriesAndSpaces={{ memories: data.memories, spaces: [] }}
|
||||
memoriesAndSpaces={{ memories: data.memories, spaces: [], userId: "" }} //TODO: Fix this please
|
||||
title={data.spaces[0]?.name}
|
||||
currentSpace={data.spaces[0]}
|
||||
usersWithAccess={hasAccess.map((x) => x.userEmail) ?? []}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import ComboboxWithCreate from "@repo/ui/shadcn/combobox";
|
|||
import { StoredSpace } from "@repo/db/schema";
|
||||
import { useKeyPress } from "@/lib/useKeyPress";
|
||||
import { useFormStatus } from "react-dom";
|
||||
import { usePendingJob } from "@/contexts/PendingJobContext";
|
||||
|
||||
function Menu() {
|
||||
useKeyPress("a", () => {
|
||||
|
|
@ -218,7 +219,7 @@ function DialogContentMenu({ setDialogClose }: { setDialogClose: () => void }) {
|
|||
setSelectedSpaces([]);
|
||||
return cont;
|
||||
};
|
||||
|
||||
const { pendingJobs, setPendingJobs } = usePendingJob();
|
||||
const formSubmit = () => {
|
||||
toast.promise(handleSubmit(content, selectedSpaces), {
|
||||
loading: (
|
||||
|
|
@ -227,8 +228,14 @@ function DialogContentMenu({ setDialogClose }: { setDialogClose: () => void }) {
|
|||
Creating memory...
|
||||
</span>
|
||||
),
|
||||
success: (data) => "Memory queued",
|
||||
error: (error) => `Memory creation failed: ${error}`,
|
||||
success: (data) => {
|
||||
setPendingJobs(pendingJobs + 1);
|
||||
return "Memory queued";
|
||||
},
|
||||
error: (error) => {
|
||||
setPendingJobs(pendingJobs - 1);
|
||||
return `Memory creation failed: ${error}`;
|
||||
},
|
||||
richColors: true,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -218,250 +218,6 @@ export const createMemory = async (input: {
|
|||
};
|
||||
}
|
||||
|
||||
// const type = typeDecider(input.content);
|
||||
|
||||
// let pageContent = input.content;
|
||||
// let metadata: Awaited<ReturnType<typeof getMetaData>>;
|
||||
// let vectorData: string;
|
||||
|
||||
// if (!(await limit(data.user.id, type))) {
|
||||
// return {
|
||||
// success: false,
|
||||
// data: 0,
|
||||
// error: `You have exceeded the limit of ${LIMITS[type as keyof typeof LIMITS]} ${type}s.`,
|
||||
// };
|
||||
// } --> How would this fit in the backend???
|
||||
|
||||
// let noteId = 0;
|
||||
|
||||
// if (type === "page") {
|
||||
// const response = await fetch("https://md.dhr.wtf/?url=" + input.content, {
|
||||
// headers: {
|
||||
// Authorization: "Bearer " + process.env.BACKEND_SECURITY_KEY,
|
||||
// },
|
||||
// });
|
||||
// pageContent = await response.text();
|
||||
// vectorData = pageContent;
|
||||
// try {
|
||||
// metadata = await getMetaData(input.content);
|
||||
// } catch (e) {
|
||||
// return {
|
||||
// success: false,
|
||||
// error: "Failed to fetch metadata for the page. Please try again later.",
|
||||
// };
|
||||
// }
|
||||
// } else if (type === "tweet") {
|
||||
// //Request the worker for the entire thread
|
||||
|
||||
// let thread: string;
|
||||
// let errorOccurred: boolean = false;
|
||||
|
||||
// try {
|
||||
// const cf_thread_endpoint = process.env.THREAD_CF_WORKER;
|
||||
// const authKey = process.env.THREAD_CF_AUTH;
|
||||
// const threadRequest = await fetch(cf_thread_endpoint, {
|
||||
// method: "POST",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// Authorization: authKey,
|
||||
// },
|
||||
// body: JSON.stringify({ url: input.content }),
|
||||
// });
|
||||
|
||||
// if (threadRequest.status !== 200) {
|
||||
// throw new Error(
|
||||
// `Failed to fetch the thread: ${input.content}, Reason: ${threadRequest.statusText}`,
|
||||
// );
|
||||
// }
|
||||
|
||||
// thread = await threadRequest.text();
|
||||
// if (thread.trim().length === 2) {
|
||||
// console.log("Thread is an empty array");
|
||||
// throw new Error(
|
||||
// "[THREAD FETCHING SERVICE] Got no content form thread worker",
|
||||
// );
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.log("[THREAD FETCHING SERVICE] Failed to fetch the thread", e);
|
||||
// errorOccurred = true;
|
||||
// }
|
||||
|
||||
// const tweet = await getTweetData(input.content.split("/").pop() as string);
|
||||
|
||||
// pageContent = tweetToMd(tweet);
|
||||
// console.log("THis ishte page content!!", pageContent);
|
||||
// //@ts-ignore
|
||||
// vectorData = errorOccurred ? JSON.stringify(pageContent) : thread;
|
||||
// metadata = {
|
||||
// baseUrl: input.content,
|
||||
// description: tweet.text.slice(0, 200),
|
||||
// image: tweet.user.profile_image_url_https,
|
||||
// title: `Tweet by ${tweet.user.name}`,
|
||||
// };
|
||||
// } else if (type === "note") {
|
||||
// pageContent = input.content;
|
||||
// vectorData = pageContent;
|
||||
// noteId = new Date().getTime();
|
||||
// metadata = {
|
||||
// baseUrl: `https://supermemory.ai/note/${noteId}`,
|
||||
// description: `Note created at ${new Date().toLocaleString()}`,
|
||||
// image: "https://supermemory.ai/logo.png",
|
||||
// title: `${pageContent.slice(0, 20)} ${pageContent.length > 20 ? "..." : ""}`,
|
||||
// };
|
||||
// } else {
|
||||
// return {
|
||||
// success: false,
|
||||
// data: 0,
|
||||
// error: "Invalid type",
|
||||
// };
|
||||
// }
|
||||
|
||||
// let storeToSpaces = input.spaces;
|
||||
|
||||
// if (!storeToSpaces) {
|
||||
// storeToSpaces = [];
|
||||
// }
|
||||
|
||||
// const vectorSaveResponse = await fetch(
|
||||
// `${process.env.BACKEND_BASE_URL}/api/add`,
|
||||
// {
|
||||
// method: "POST",
|
||||
// body: JSON.stringify({
|
||||
// pageContent: vectorData,
|
||||
// title: metadata.title,
|
||||
// description: metadata.description,
|
||||
// url: metadata.baseUrl,
|
||||
// spaces: storeToSpaces.map((spaceId) => spaceId.toString()),
|
||||
// user: data.user.id,
|
||||
// type,
|
||||
// }),
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// Authorization: "Bearer " + process.env.BACKEND_SECURITY_KEY,
|
||||
// },
|
||||
// },
|
||||
// );
|
||||
|
||||
// if (!vectorSaveResponse.ok) {
|
||||
// const errorData = await vectorSaveResponse.text();
|
||||
// console.error(errorData);
|
||||
// return {
|
||||
// success: false,
|
||||
// data: 0,
|
||||
// error: `Failed to save to vector store. Backend returned error: ${errorData}`,
|
||||
// };
|
||||
// }
|
||||
|
||||
// let contentId: number;
|
||||
|
||||
// const response = (await vectorSaveResponse.json()) as {
|
||||
// status: string;
|
||||
// chunkedInput: string;
|
||||
// message?: string;
|
||||
// };
|
||||
|
||||
// try {
|
||||
// if (response.status !== "ok") {
|
||||
// if (response.status === "error") {
|
||||
// return {
|
||||
// success: false,
|
||||
// data: 0,
|
||||
// error: response.message,
|
||||
// };
|
||||
// } else {
|
||||
// return {
|
||||
// success: false,
|
||||
// data: 0,
|
||||
// error: `Failed to save to vector store. Backend returned error: ${response.message}`,
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// } catch (e) {
|
||||
// return {
|
||||
// success: false,
|
||||
// data: 0,
|
||||
// error: `Failed to save to vector store. Backend returned error: ${e}`,
|
||||
// };
|
||||
// }
|
||||
|
||||
// const saveToDbUrl =
|
||||
// (metadata.baseUrl.split("#supermemory-user-")[0] ?? metadata.baseUrl) +
|
||||
// "#supermemory-user-" +
|
||||
// data.user.id;
|
||||
|
||||
// // Insert into database
|
||||
// try {
|
||||
// const insertResponse = await db
|
||||
// .insert(storedContent)
|
||||
// .values({
|
||||
// content: pageContent,
|
||||
// title: metadata.title,
|
||||
// description: metadata.description,
|
||||
// url: saveToDbUrl,
|
||||
// baseUrl: saveToDbUrl,
|
||||
// image: metadata.image,
|
||||
// savedAt: new Date(),
|
||||
// userId: data.user.id,
|
||||
// type,
|
||||
// noteId,
|
||||
// })
|
||||
// .returning({ id: storedContent.id });
|
||||
// revalidatePath("/memories");
|
||||
// revalidatePath("/home");
|
||||
|
||||
// if (!insertResponse[0]?.id) {
|
||||
// return {
|
||||
// success: false,
|
||||
// data: 0,
|
||||
// error: "Something went wrong while saving the document to the database",
|
||||
// };
|
||||
// }
|
||||
|
||||
// contentId = insertResponse[0]?.id;
|
||||
// } catch (e) {
|
||||
// const error = e as Error;
|
||||
// console.log("Error: ", error.message);
|
||||
|
||||
// if (
|
||||
// error.message.includes(
|
||||
// "D1_ERROR: UNIQUE constraint failed: storedContent.baseUrl",
|
||||
// )
|
||||
// ) {
|
||||
// return {
|
||||
// success: false,
|
||||
// data: 0,
|
||||
// error: "Content already exists",
|
||||
// };
|
||||
// }
|
||||
|
||||
// return {
|
||||
// success: false,
|
||||
// data: 0,
|
||||
// error: "Failed to save to database with error: " + error.message,
|
||||
// };
|
||||
// }
|
||||
|
||||
// if (storeToSpaces.length > 0) {
|
||||
// // Adding the many-to-many relationship between content and spaces
|
||||
// const spaceData = await db
|
||||
// .select()
|
||||
// .from(space)
|
||||
// .where(
|
||||
// and(inArray(space.id, storeToSpaces), eq(space.user, data.user.id)),
|
||||
// )
|
||||
// .all();
|
||||
|
||||
// await Promise.all(
|
||||
// spaceData.map(async (s) => {
|
||||
// await db
|
||||
// .insert(contentToSpace)
|
||||
// .values({ contentId: contentId, spaceId: s.id });
|
||||
|
||||
// await db.update(space).set({ numItems: s.numItems + 1 });
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: 1,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
chatThreads,
|
||||
Content,
|
||||
contentToSpace,
|
||||
Job,
|
||||
space,
|
||||
spacesAccess,
|
||||
storedContent,
|
||||
|
|
@ -158,10 +159,10 @@ export const getMemoriesInsideSpace = async (
|
|||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getAllUserMemoriesAndSpaces = async (): ServerActionReturnType<{
|
||||
spaces: StoredSpace[];
|
||||
memories: Content[];
|
||||
userId: string;
|
||||
}> => {
|
||||
const data = await auth();
|
||||
|
||||
|
|
|
|||
33
apps/web/app/api/getJobCount/route.ts
Normal file
33
apps/web/app/api/getJobCount/route.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { db } from "@/server/db";
|
||||
import { and, eq, sql } from "drizzle-orm";
|
||||
import { jobs } from "@repo/db/schema";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { ensureAuth } from "../ensureAuth";
|
||||
|
||||
export const runtime = "edge";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const session = await ensureAuth(req);
|
||||
|
||||
if (!session) {
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
// Calculate the timestamp for one hour ago
|
||||
const oneHourAgoTimestamp = Math.floor(Date.now() / 1000) - 3600;
|
||||
|
||||
const jobCount = await db
|
||||
.select({ count: sql<number>`count(*)`.mapWith(Number) })
|
||||
.from(jobs)
|
||||
.where(
|
||||
and(
|
||||
eq(jobs.userId, session.user.id),
|
||||
eq(jobs.status, "Processing"),
|
||||
sql`${jobs.updatedAt} > ${oneHourAgoTimestamp}`,
|
||||
),
|
||||
);
|
||||
|
||||
return NextResponse.json({
|
||||
pendingJobs: jobCount[0]?.count ?? 0,
|
||||
});
|
||||
}
|
||||
46
apps/web/app/api/getNewMemories/route.ts
Normal file
46
apps/web/app/api/getNewMemories/route.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { db } from "@/server/db";
|
||||
import { storedContent } from "@repo/db/schema";
|
||||
import { eq, gt } from "drizzle-orm";
|
||||
import { ensureAuth } from "../ensureAuth";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export const runtime = "edge";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const session = await ensureAuth(req);
|
||||
|
||||
if (!session) {
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
const lastFetchTimestamp = req.nextUrl.searchParams.get("lastFetchTimestamp");
|
||||
|
||||
if (!lastFetchTimestamp) {
|
||||
return NextResponse.json(
|
||||
{ error: "Missing required parameters" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
if (!lastFetchTimestamp) {
|
||||
return NextResponse.json(
|
||||
{ error: "Missing required parameters" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const newMemories = await db.query.storedContent.findMany({
|
||||
where: (content) =>
|
||||
eq(content.userId, session.user.id) &&
|
||||
gt(content.savedAt, new Date(lastFetchTimestamp)),
|
||||
});
|
||||
|
||||
return NextResponse.json({ newMemories });
|
||||
} catch (error) {
|
||||
console.error("Error fetching new memories:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Internal server error" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
52
apps/web/app/api/store/mobile/route.ts
Normal file
52
apps/web/app/api/store/mobile/route.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { NextRequest } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { ensureAuth } from "../../ensureAuth";
|
||||
|
||||
export const runtime = "edge";
|
||||
|
||||
const vectorBody = z.object({
|
||||
spaces: z.array(z.number()).optional(),
|
||||
url: z.string(),
|
||||
});
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const session = await ensureAuth(req);
|
||||
|
||||
if (!session) {
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await req.json();
|
||||
const validatedBody = vectorBody.parse(body);
|
||||
const vectorSaveResponses = await fetch(
|
||||
`${process.env.BACKEND_BASE_URL}/api/add`,
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
url: validatedBody.url,
|
||||
spaces: validatedBody.spaces,
|
||||
user: session.user.id,
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer " + process.env.BACKEND_SECURITY_KEY,
|
||||
},
|
||||
},
|
||||
);
|
||||
const response = (await vectorSaveResponses.json()) as {
|
||||
status: string;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
if (response.status !== "ok") {
|
||||
return new Response("Internal server error", { status: 500 });
|
||||
}
|
||||
|
||||
if (response.status === "ok") {
|
||||
return new Response("Added to queue", { status: 200 });
|
||||
}
|
||||
} catch (e) {
|
||||
return new Response("Bad Request", { status: 400 });
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import { GeistSans } from "geist/font/sans";
|
|||
import { GeistMono } from "geist/font/mono";
|
||||
import { cn } from "@repo/ui/lib/utils";
|
||||
import { Toaster } from "@repo/ui/shadcn/toaster";
|
||||
import { PHProvider } from "./providers";
|
||||
import { PendingJobsProvider, PHProvider } from "./providers";
|
||||
import PostHogPageView from "./PHPageViews";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
|
@ -78,17 +78,19 @@ export default function RootLayout({
|
|||
></script>
|
||||
</head>
|
||||
<PHProvider>
|
||||
<body
|
||||
className={cn(
|
||||
`${inter.className} dark`,
|
||||
GeistMono.variable,
|
||||
GeistSans.variable,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
<Toaster />
|
||||
{/* <PostHogPageView /> */}
|
||||
</body>
|
||||
<PendingJobsProvider>
|
||||
<body
|
||||
className={cn(
|
||||
`${inter.className} dark`,
|
||||
GeistMono.variable,
|
||||
GeistSans.variable,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
<Toaster />
|
||||
{/* <PostHogPageView /> */}
|
||||
</body>
|
||||
</PendingJobsProvider>
|
||||
</PHProvider>
|
||||
</html>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// app/providers.tsx
|
||||
"use client";
|
||||
import { PendingJobProvider } from "@/contexts/PendingJobContext";
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider } from "posthog-js/react";
|
||||
|
||||
|
|
@ -14,3 +15,11 @@ if (typeof window !== "undefined") {
|
|||
export function PHProvider({ children }: { children: React.ReactNode }) {
|
||||
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
|
||||
}
|
||||
|
||||
export function PendingJobsProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return <PendingJobProvider>{children}</PendingJobProvider>;
|
||||
}
|
||||
|
|
|
|||
62
apps/web/components/loadingCard/loadingCard.tsx
Normal file
62
apps/web/components/loadingCard/loadingCard.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { motion } from "framer-motion";
|
||||
|
||||
const LoadingCard = ({ pendingJobs }: { pendingJobs: number }) => {
|
||||
if (pendingJobs === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="relative bg-[#2D2D2D] rounded-xl overflow-hidden w-full min-h-[100px] max-h-[200px]"
|
||||
animate={{
|
||||
boxShadow: [
|
||||
"0 0 0 2px rgba(186, 66, 255, 0.5)",
|
||||
"0 0 0 2px rgba(0, 225, 255, 0.8)",
|
||||
"0 0 0 2px rgba(186, 66, 255, 0.5)",
|
||||
],
|
||||
}}
|
||||
transition={{
|
||||
duration: 3,
|
||||
repeat: Infinity,
|
||||
ease: "linear",
|
||||
}}
|
||||
>
|
||||
<div className="absolute inset-0.5 bg-[#2D2D2D] rounded-xl z-10"></div>
|
||||
<div className="relative z-20 flex flex-col items-center justify-center h-full text-white p-4">
|
||||
<motion.div
|
||||
className="w-12 h-12 mb-2 rounded-full relative"
|
||||
style={{
|
||||
backgroundImage:
|
||||
"linear-gradient(rgb(186, 66, 255) 35%, rgb(0, 225, 255))",
|
||||
filter: "blur(1px)",
|
||||
boxShadow:
|
||||
"0px -4px 16px 0px rgb(186, 66, 255), 0px 4px 16px 0px rgb(0, 225, 255)",
|
||||
}}
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{
|
||||
duration: 1.7,
|
||||
repeat: Infinity,
|
||||
ease: "linear",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="absolute inset-0 rounded-full"
|
||||
style={{
|
||||
backgroundColor: "rgb(36, 36, 36)",
|
||||
|
||||
filter: "blur(8px)",
|
||||
}}
|
||||
></div>
|
||||
</motion.div>
|
||||
|
||||
<p className="text-base font-semibold text-center">One moment</p>
|
||||
<p className="text-xs text-gray-400 text-center">
|
||||
Saving {pendingJobs} {pendingJobs === 1 ? "memory" : "memories"} for
|
||||
you
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingCard;
|
||||
31
apps/web/contexts/PendingJobContext.tsx
Normal file
31
apps/web/contexts/PendingJobContext.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
"use client";
|
||||
import React, { createContext, useState, useContext } from "react";
|
||||
|
||||
interface PendingJobContextType {
|
||||
pendingJobs: number;
|
||||
setPendingJobs: (pendingJobs: number) => void;
|
||||
}
|
||||
|
||||
const PendingJobContext = createContext<PendingJobContextType | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
export const PendingJobProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [pendingJobs, setPendingJobs] = useState(0);
|
||||
|
||||
return (
|
||||
<PendingJobContext.Provider value={{ pendingJobs, setPendingJobs }}>
|
||||
{children}
|
||||
</PendingJobContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const usePendingJob = () => {
|
||||
const context = useContext(PendingJobContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("usePendingJob must be used within a PendingJobProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
|
@ -43,7 +43,7 @@ CREATE TABLE `chatHistory` (
|
|||
`answerParts` text,
|
||||
`answerSources` text,
|
||||
`answerJustification` text,
|
||||
`createdAt` integer DEFAULT '"2024-07-31T07:35:53.819Z"' NOT NULL,
|
||||
`createdAt` integer DEFAULT '"2024-09-05T18:04:25.816Z"' NOT NULL,
|
||||
FOREIGN KEY (`threadId`) REFERENCES `chatThread`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
|
|
@ -70,8 +70,8 @@ CREATE TABLE `jobs` (
|
|||
`attempts` integer DEFAULT 0 NOT NULL,
|
||||
`lastAttemptAt` integer,
|
||||
`error` blob,
|
||||
`createdAt` integer NOT NULL,
|
||||
`updatedAt` integer NOT NULL,
|
||||
`createdAt` integer DEFAULT '"2024-09-05T18:04:25.816Z"' NOT NULL,
|
||||
`updatedAt` integer DEFAULT '"2024-09-05T18:04:25.816Z"' NOT NULL,
|
||||
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
|
|
@ -105,8 +105,8 @@ CREATE TABLE `storedContent` (
|
|||
`description` text(255),
|
||||
`url` text NOT NULL,
|
||||
`savedAt` integer NOT NULL,
|
||||
`baseUrl` text(255),
|
||||
`ogImage` text(255),
|
||||
`baseUrl` text,
|
||||
`ogImage` text,
|
||||
`type` text DEFAULT 'page',
|
||||
`image` text(255),
|
||||
`user` text,
|
||||
|
|
@ -142,7 +142,6 @@ CREATE INDEX `jobs_url_idx` ON `jobs` (`url`);--> statement-breakpoint
|
|||
CREATE UNIQUE INDEX `space_name_unique` ON `space` (`name`);--> statement-breakpoint
|
||||
CREATE INDEX `spaces_name_idx` ON `space` (`name`);--> statement-breakpoint
|
||||
CREATE INDEX `spaces_user_idx` ON `space` (`user`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `storedContent_baseUrl_unique` ON `storedContent` (`baseUrl`);--> statement-breakpoint
|
||||
CREATE INDEX `storedContent_url_idx` ON `storedContent` (`url`);--> statement-breakpoint
|
||||
CREATE INDEX `storedContent_savedAt_idx` ON `storedContent` (`savedAt`);--> statement-breakpoint
|
||||
CREATE INDEX `storedContent_title_idx` ON `storedContent` (`title`);--> statement-breakpoint
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,923 +0,0 @@
|
|||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "30bdc8fa-70a7-44b7-a06b-59b950a62c11",
|
||||
"prevId": "3fbdb153-2764-4b09-ac22-05c3a131ec35",
|
||||
"tables": {
|
||||
"account": {
|
||||
"name": "account",
|
||||
"columns": {
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"providerAccountId": {
|
||||
"name": "providerAccountId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"token_type": {
|
||||
"name": "token_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"scope": {
|
||||
"name": "scope",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"id_token": {
|
||||
"name": "id_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"session_state": {
|
||||
"name": "session_state",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"account_userId_user_id_fk": {
|
||||
"name": "account_userId_user_id_fk",
|
||||
"tableFrom": "account",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"account_provider_providerAccountId_pk": {
|
||||
"columns": ["provider", "providerAccountId"],
|
||||
"name": "account_provider_providerAccountId_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"authenticator": {
|
||||
"name": "authenticator",
|
||||
"columns": {
|
||||
"credentialID": {
|
||||
"name": "credentialID",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"providerAccountId": {
|
||||
"name": "providerAccountId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"credentialPublicKey": {
|
||||
"name": "credentialPublicKey",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"counter": {
|
||||
"name": "counter",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"credentialDeviceType": {
|
||||
"name": "credentialDeviceType",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"credentialBackedUp": {
|
||||
"name": "credentialBackedUp",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"transports": {
|
||||
"name": "transports",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"authenticator_credentialID_unique": {
|
||||
"name": "authenticator_credentialID_unique",
|
||||
"columns": ["credentialID"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"authenticator_userId_user_id_fk": {
|
||||
"name": "authenticator_userId_user_id_fk",
|
||||
"tableFrom": "authenticator",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"authenticator_userId_credentialID_pk": {
|
||||
"columns": ["credentialID", "userId"],
|
||||
"name": "authenticator_userId_credentialID_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"canvas": {
|
||||
"name": "canvas",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'Untitled'"
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'Untitled'"
|
||||
},
|
||||
"url": {
|
||||
"name": "url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "''"
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"canvas_user_userId": {
|
||||
"name": "canvas_user_userId",
|
||||
"columns": ["userId"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"canvas_userId_user_id_fk": {
|
||||
"name": "canvas_userId_user_id_fk",
|
||||
"tableFrom": "canvas",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"chatHistory": {
|
||||
"name": "chatHistory",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"threadId": {
|
||||
"name": "threadId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"question": {
|
||||
"name": "question",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"answerParts": {
|
||||
"name": "answerParts",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"answerSources": {
|
||||
"name": "answerSources",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"answerJustification": {
|
||||
"name": "answerJustification",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'\"2024-08-27T18:12:22.062Z\"'"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"chatHistory_thread_idx": {
|
||||
"name": "chatHistory_thread_idx",
|
||||
"columns": ["threadId"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"chatHistory_threadId_chatThread_id_fk": {
|
||||
"name": "chatHistory_threadId_chatThread_id_fk",
|
||||
"tableFrom": "chatHistory",
|
||||
"tableTo": "chatThread",
|
||||
"columnsFrom": ["threadId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"chatThread": {
|
||||
"name": "chatThread",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"firstMessage": {
|
||||
"name": "firstMessage",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"chatThread_user_idx": {
|
||||
"name": "chatThread_user_idx",
|
||||
"columns": ["userId"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"chatThread_userId_user_id_fk": {
|
||||
"name": "chatThread_userId_user_id_fk",
|
||||
"tableFrom": "chatThread",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"contentToSpace": {
|
||||
"name": "contentToSpace",
|
||||
"columns": {
|
||||
"contentId": {
|
||||
"name": "contentId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"spaceId": {
|
||||
"name": "spaceId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"contentToSpace_contentId_storedContent_id_fk": {
|
||||
"name": "contentToSpace_contentId_storedContent_id_fk",
|
||||
"tableFrom": "contentToSpace",
|
||||
"tableTo": "storedContent",
|
||||
"columnsFrom": ["contentId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"contentToSpace_spaceId_space_id_fk": {
|
||||
"name": "contentToSpace_spaceId_space_id_fk",
|
||||
"tableFrom": "contentToSpace",
|
||||
"tableTo": "space",
|
||||
"columnsFrom": ["spaceId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"contentToSpace_contentId_spaceId_pk": {
|
||||
"columns": ["contentId", "spaceId"],
|
||||
"name": "contentToSpace_contentId_spaceId_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"jobs": {
|
||||
"name": "jobs",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"url": {
|
||||
"name": "url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"attempts": {
|
||||
"name": "attempts",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"lastAttemptAt": {
|
||||
"name": "lastAttemptAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"error": {
|
||||
"name": "error",
|
||||
"type": "blob",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'\"2024-08-27T18:12:22.062Z\"'"
|
||||
},
|
||||
"updatedAt": {
|
||||
"name": "updatedAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'\"2024-08-27T18:12:22.062Z\"'"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"jobs_userId_idx": {
|
||||
"name": "jobs_userId_idx",
|
||||
"columns": ["userId"],
|
||||
"isUnique": false
|
||||
},
|
||||
"jobs_status_idx": {
|
||||
"name": "jobs_status_idx",
|
||||
"columns": ["status"],
|
||||
"isUnique": false
|
||||
},
|
||||
"jobs_createdAt_idx": {
|
||||
"name": "jobs_createdAt_idx",
|
||||
"columns": ["createdAt"],
|
||||
"isUnique": false
|
||||
},
|
||||
"jobs_url_idx": {
|
||||
"name": "jobs_url_idx",
|
||||
"columns": ["url"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"jobs_userId_user_id_fk": {
|
||||
"name": "jobs_userId_user_id_fk",
|
||||
"tableFrom": "jobs",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"session": {
|
||||
"name": "session",
|
||||
"columns": {
|
||||
"sessionToken": {
|
||||
"name": "sessionToken",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires": {
|
||||
"name": "expires",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"session_userId_user_id_fk": {
|
||||
"name": "session_userId_user_id_fk",
|
||||
"tableFrom": "session",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"space": {
|
||||
"name": "space",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'none'"
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"type": "text(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"numItems": {
|
||||
"name": "numItems",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"space_name_unique": {
|
||||
"name": "space_name_unique",
|
||||
"columns": ["name"],
|
||||
"isUnique": true
|
||||
},
|
||||
"spaces_name_idx": {
|
||||
"name": "spaces_name_idx",
|
||||
"columns": ["name"],
|
||||
"isUnique": false
|
||||
},
|
||||
"spaces_user_idx": {
|
||||
"name": "spaces_user_idx",
|
||||
"columns": ["user"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"space_user_user_id_fk": {
|
||||
"name": "space_user_user_id_fk",
|
||||
"tableFrom": "space",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": ["user"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"spacesAccess": {
|
||||
"name": "spacesAccess",
|
||||
"columns": {
|
||||
"spaceId": {
|
||||
"name": "spaceId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"userEmail": {
|
||||
"name": "userEmail",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"spacesAccess_spaceId_space_id_fk": {
|
||||
"name": "spacesAccess_spaceId_space_id_fk",
|
||||
"tableFrom": "spacesAccess",
|
||||
"tableTo": "space",
|
||||
"columnsFrom": ["spaceId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"spacesAccess_spaceId_userEmail_pk": {
|
||||
"columns": ["spaceId", "userEmail"],
|
||||
"name": "spacesAccess_spaceId_userEmail_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"storedContent": {
|
||||
"name": "storedContent",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"url": {
|
||||
"name": "url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"savedAt": {
|
||||
"name": "savedAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"baseUrl": {
|
||||
"name": "baseUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"ogImage": {
|
||||
"name": "ogImage",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "'page'"
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"type": "text(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"noteId": {
|
||||
"name": "noteId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"storedContent_url_idx": {
|
||||
"name": "storedContent_url_idx",
|
||||
"columns": ["url"],
|
||||
"isUnique": false
|
||||
},
|
||||
"storedContent_savedAt_idx": {
|
||||
"name": "storedContent_savedAt_idx",
|
||||
"columns": ["savedAt"],
|
||||
"isUnique": false
|
||||
},
|
||||
"storedContent_title_idx": {
|
||||
"name": "storedContent_title_idx",
|
||||
"columns": ["title"],
|
||||
"isUnique": false
|
||||
},
|
||||
"storedContent_user_idx": {
|
||||
"name": "storedContent_user_idx",
|
||||
"columns": ["user"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"storedContent_user_user_id_fk": {
|
||||
"name": "storedContent_user_user_id_fk",
|
||||
"tableFrom": "storedContent",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": ["user"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"emailVerified": {
|
||||
"name": "emailVerified",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"telegramId": {
|
||||
"name": "telegramId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"hasOnboarded": {
|
||||
"name": "hasOnboarded",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"users_email_idx": {
|
||||
"name": "users_email_idx",
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
},
|
||||
"users_telegram_idx": {
|
||||
"name": "users_telegram_idx",
|
||||
"columns": ["telegramId"],
|
||||
"isUnique": false
|
||||
},
|
||||
"users_id_idx": {
|
||||
"name": "users_id_idx",
|
||||
"columns": ["id"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"verificationToken": {
|
||||
"name": "verificationToken",
|
||||
"columns": {
|
||||
"identifier": {
|
||||
"name": "identifier",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"token": {
|
||||
"name": "token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires": {
|
||||
"name": "expires",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"verificationToken_identifier_token_pk": {
|
||||
"columns": ["identifier", "token"],
|
||||
"name": "verificationToken_identifier_token_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,13 @@
|
|||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1722411353835,
|
||||
"tag": "0000_fixed_pandemic",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "6",
|
||||
"when": 1724782342070,
|
||||
"tag": "0001_huge_puma",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1725559465821,
|
||||
"tag": "0000_adorable_iron_man",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
function convertStringToFixedNumber(input: string): number {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
for (let i = 0; i < input?.length; i++) {
|
||||
const char = input.charCodeAt(i);
|
||||
hash = (hash * 31 + char) % 1000000007; // Hashing by a large prime number
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ function random(seed: number) {
|
|||
}
|
||||
|
||||
function shuffle(array: string[], seed: number) {
|
||||
let m = array.length,
|
||||
let m = array?.length,
|
||||
t,
|
||||
i;
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ function shuffle(array: string[], seed: number) {
|
|||
export const cipher = (text: string) => {
|
||||
let returned_text = "";
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
for (let i = 0; i < text?.length; i++) {
|
||||
returned_text += shuffled[chars.indexOf(text[i]!)];
|
||||
}
|
||||
|
||||
|
|
@ -49,19 +49,19 @@ export const decipher = (text: string) => {
|
|||
let returned_text = "";
|
||||
const index = Math.floor(
|
||||
random(convertStringToFixedNumber(process.env.BACKEND_SECURITY_KEY)) *
|
||||
(text.length / 2),
|
||||
(text?.length / 2),
|
||||
);
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
for (let i = 0; i < text?.length; i++) {
|
||||
returned_text += chars[shuffled.indexOf(text[i]!)];
|
||||
}
|
||||
const total = parseInt(text[index]!);
|
||||
const str = parseInt(text.slice(index + 1, index + total + 1));
|
||||
return returned_text.slice(text.length - str);
|
||||
return returned_text.slice(text?.length - str);
|
||||
};
|
||||
|
||||
const extend = (text: string, length = 60) => {
|
||||
const extra = length - text.length;
|
||||
const extra = length - text?.length;
|
||||
|
||||
if (extra < 0) {
|
||||
return text;
|
||||
|
|
@ -74,9 +74,9 @@ const extend = (text: string, length = 60) => {
|
|||
);
|
||||
|
||||
const storage_string =
|
||||
text.length.toString().length.toString() + text.length.toString();
|
||||
text?.length.toString()?.length.toString() + text?.length.toString();
|
||||
let returned = "";
|
||||
let total = storage_string.length + text.length;
|
||||
let total = storage_string?.length + text?.length;
|
||||
|
||||
for (let i = 0; i < extra; i++) {
|
||||
if (i == index) {
|
||||
|
|
@ -86,7 +86,8 @@ const extend = (text: string, length = 60) => {
|
|||
break;
|
||||
}
|
||||
// Add a random character
|
||||
returned += shuffled[Math.floor(random(Math.random()) * shuffled.length)];
|
||||
returned +=
|
||||
shuffled[Math.floor(random(Math.random()) * shuffled?.length)];
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue