Spaces:
Running
Running
share roast feature
Browse files- Dockerfile +12 -4
- app/[roastId]/page.tsx +50 -0
- app/actions/roast.ts +20 -0
- app/actions/share.ts +21 -0
- app/api/roast/route.ts +0 -16
- app/layout.tsx +5 -1
- app/page.tsx +38 -23
- components/copy.tsx +47 -0
- components/quote.tsx +22 -0
- entrypoint.sh +2 -0
- package-lock.json +77 -1
- package.json +2 -0
- prisma/dev.db +0 -0
- prisma/schema.prisma +14 -0
- utils/prisma.ts +4 -0
- utils/roast copy.ts +0 -55
Dockerfile
CHANGED
@@ -1,24 +1,32 @@
|
|
1 |
# Dockerfile
|
|
|
2 |
# Use an official Node.js runtime as the base image
|
3 |
FROM node:18
|
4 |
|
|
|
|
|
5 |
# Set the working directory in the container
|
6 |
WORKDIR /usr/src/app
|
7 |
|
8 |
# Copy package.json and package-lock.json to the container
|
9 |
-
COPY package.json package-lock.json ./
|
10 |
|
11 |
# Install dependencies
|
12 |
RUN npm install
|
13 |
|
|
|
|
|
14 |
# Copy the rest of the application files to the container
|
15 |
-
COPY . .
|
|
|
16 |
|
17 |
# Build the Next.js application for production
|
18 |
-
RUN npm run build
|
19 |
|
20 |
# Expose the application port (assuming your app runs on port 3000)
|
21 |
EXPOSE 3000
|
22 |
|
|
|
|
|
23 |
# Start the application
|
24 |
-
|
|
|
1 |
# Dockerfile
|
2 |
+
|
3 |
# Use an official Node.js runtime as the base image
|
4 |
FROM node:18
|
5 |
|
6 |
+
USER 1000
|
7 |
+
|
8 |
# Set the working directory in the container
|
9 |
WORKDIR /usr/src/app
|
10 |
|
11 |
# Copy package.json and package-lock.json to the container
|
12 |
+
COPY --chown=1000 package.json package-lock.json ./
|
13 |
|
14 |
# Install dependencies
|
15 |
RUN npm install
|
16 |
|
17 |
+
VOLUME /data
|
18 |
+
|
19 |
# Copy the rest of the application files to the container
|
20 |
+
COPY --chown=1000 . .
|
21 |
+
RUN chmod +x entrypoint.sh
|
22 |
|
23 |
# Build the Next.js application for production
|
24 |
+
# RUN npm run build
|
25 |
|
26 |
# Expose the application port (assuming your app runs on port 3000)
|
27 |
EXPOSE 3000
|
28 |
|
29 |
+
RUN npx prisma generate
|
30 |
+
|
31 |
# Start the application
|
32 |
+
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
|
app/[roastId]/page.tsx
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { redirect } from "next/navigation";
|
2 |
+
import Image from "next/image";
|
3 |
+
|
4 |
+
import { getRoast } from "@/app/actions/roast";
|
5 |
+
import { Quote } from "@/components/quote";
|
6 |
+
import Logo from "@/assets/logo.svg";
|
7 |
+
import Link from "next/link";
|
8 |
+
|
9 |
+
async function get(id: string) {
|
10 |
+
const roast = await getRoast({ id });
|
11 |
+
return roast;
|
12 |
+
}
|
13 |
+
|
14 |
+
export default async function Roast({
|
15 |
+
params: { roastId },
|
16 |
+
}: {
|
17 |
+
params: { roastId: string };
|
18 |
+
}) {
|
19 |
+
const quote = await get(roastId);
|
20 |
+
|
21 |
+
if (!quote?.data) {
|
22 |
+
redirect("/");
|
23 |
+
}
|
24 |
+
return (
|
25 |
+
<div>
|
26 |
+
<header className="flex items-start max-lg:gap-1 lg:items-center justify-between max-lg:flex-col border-b border-zinc-200 pb-5">
|
27 |
+
<Image
|
28 |
+
src={Logo}
|
29 |
+
alt="logo hugging face"
|
30 |
+
width={100}
|
31 |
+
height={100}
|
32 |
+
className="object-contain w-36 lg:w-44"
|
33 |
+
/>
|
34 |
+
<div>
|
35 |
+
<p className="text-sm text-zinc-500">
|
36 |
+
Roast your favorite Hugging Face user! 👹
|
37 |
+
</p>
|
38 |
+
</div>
|
39 |
+
</header>
|
40 |
+
<Quote data={quote.data?.text}>
|
41 |
+
<Link
|
42 |
+
href="/"
|
43 |
+
className="bg-black rounded-full inline-block px-4 py-2.5 text-sm font-medium text-white hover:bg-zinc-800 mt-5"
|
44 |
+
>
|
45 |
+
Roast another user! 🧨
|
46 |
+
</Link>
|
47 |
+
</Quote>
|
48 |
+
</div>
|
49 |
+
);
|
50 |
+
}
|
app/actions/roast.ts
CHANGED
@@ -5,6 +5,7 @@ import { HfInference } from '@huggingface/inference'
|
|
5 |
|
6 |
import { formatInformations, transformForInference } from "@/utils/roast";
|
7 |
import { FormProps } from "@/components/form";
|
|
|
8 |
|
9 |
const MODEL_ID = "meta-llama/Meta-Llama-3.1-70B-Instruct";
|
10 |
|
@@ -60,4 +61,23 @@ export async function roast({ username, language }: FormProps) {
|
|
60 |
return {
|
61 |
data: res.generated_text
|
62 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
}
|
|
|
5 |
|
6 |
import { formatInformations, transformForInference } from "@/utils/roast";
|
7 |
import { FormProps } from "@/components/form";
|
8 |
+
import prisma from "@/utils/prisma";
|
9 |
|
10 |
const MODEL_ID = "meta-llama/Meta-Llama-3.1-70B-Instruct";
|
11 |
|
|
|
61 |
return {
|
62 |
data: res.generated_text
|
63 |
}
|
64 |
+
}
|
65 |
+
|
66 |
+
export async function getRoast({ id } : { id: string }) {
|
67 |
+
const roast = await prisma.quote.findUnique({
|
68 |
+
where: {
|
69 |
+
id
|
70 |
+
}
|
71 |
+
})
|
72 |
+
|
73 |
+
if (!roast) {
|
74 |
+
return {
|
75 |
+
error: "Roast not found",
|
76 |
+
status: 404
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
return {
|
81 |
+
data: roast
|
82 |
+
}
|
83 |
}
|
app/actions/share.ts
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use server";
|
2 |
+
import prisma from "@/utils/prisma";
|
3 |
+
|
4 |
+
export interface ShareProps {
|
5 |
+
hf_user: string;
|
6 |
+
text: string;
|
7 |
+
}
|
8 |
+
|
9 |
+
export async function share(form: ShareProps) {
|
10 |
+
|
11 |
+
const quote = await prisma.quote.create({
|
12 |
+
data: {
|
13 |
+
hf_user: form.hf_user,
|
14 |
+
text: form.text
|
15 |
+
}
|
16 |
+
})
|
17 |
+
|
18 |
+
return {
|
19 |
+
data: quote
|
20 |
+
}
|
21 |
+
}
|
app/api/roast/route.ts
DELETED
@@ -1,16 +0,0 @@
|
|
1 |
-
import { NextResponse } from "next/server";
|
2 |
-
|
3 |
-
export async function POST(req: Request) {
|
4 |
-
const body = await req.json();
|
5 |
-
console.log(body);
|
6 |
-
|
7 |
-
// fetch the user's username from the body
|
8 |
-
const { username } = body;
|
9 |
-
|
10 |
-
// fetch user from hugging face API
|
11 |
-
const user = await fetch(`https://huggingface.co/api/users/${username}`);
|
12 |
-
|
13 |
-
console.log(user);
|
14 |
-
|
15 |
-
return NextResponse.json({ message: "Roasted!" });
|
16 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/layout.tsx
CHANGED
@@ -16,7 +16,11 @@ export default function RootLayout({
|
|
16 |
}>) {
|
17 |
return (
|
18 |
<html lang="en">
|
19 |
-
<body className={inter.className}>
|
|
|
|
|
|
|
|
|
20 |
</html>
|
21 |
);
|
22 |
}
|
|
|
16 |
}>) {
|
17 |
return (
|
18 |
<html lang="en">
|
19 |
+
<body className={inter.className}>
|
20 |
+
<section className="min-h-screen h-full w-full flex items-center justify-center flex-col bg-zinc-100 gap-5 overflow-auto p-6">
|
21 |
+
{children}
|
22 |
+
</section>
|
23 |
+
</body>
|
24 |
</html>
|
25 |
);
|
26 |
}
|
app/page.tsx
CHANGED
@@ -4,7 +4,10 @@ import Image from "next/image";
|
|
4 |
import classNames from "classnames";
|
5 |
|
6 |
import { roast } from "@/app/actions/roast";
|
|
|
7 |
import { Form, FormProps } from "@/components/form";
|
|
|
|
|
8 |
|
9 |
import Logo from "@/assets/logo.svg";
|
10 |
|
@@ -12,6 +15,9 @@ export default function Home() {
|
|
12 |
const [data, setData] = useState("");
|
13 |
const [error, setError] = useState("");
|
14 |
const [loading, setLoading] = useState(false);
|
|
|
|
|
|
|
15 |
|
16 |
const handleRoast = async (form: FormProps) => {
|
17 |
setError("");
|
@@ -26,14 +32,25 @@ export default function Home() {
|
|
26 |
if (res.error) {
|
27 |
setError(res.error);
|
28 |
} else {
|
|
|
|
|
29 |
setData(res?.data);
|
30 |
}
|
31 |
|
32 |
setLoading(false);
|
33 |
};
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
return (
|
36 |
-
|
37 |
<div className="max-w-2xl w-full border border-gray-200 bg-white rounded-3xl p-8 grid gap-8 shadow-xl shadow-black/5">
|
38 |
<header className="flex items-start max-lg:gap-1 lg:items-center justify-between max-lg:flex-col border-b border-zinc-200 pb-5">
|
39 |
<Image
|
@@ -58,29 +75,27 @@ export default function Home() {
|
|
58 |
<Form loading={loading} onSubmit={handleRoast} />
|
59 |
</div>
|
60 |
{data && (
|
61 |
-
<
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
// "animate-pulse": loading,
|
76 |
}
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
</div>
|
83 |
)}
|
84 |
-
|
85 |
);
|
86 |
}
|
|
|
4 |
import classNames from "classnames";
|
5 |
|
6 |
import { roast } from "@/app/actions/roast";
|
7 |
+
import { share, ShareProps } from "@/app/actions/share";
|
8 |
import { Form, FormProps } from "@/components/form";
|
9 |
+
import { CopyToClipboard } from "@/components/copy";
|
10 |
+
import { Quote } from "@/components/quote";
|
11 |
|
12 |
import Logo from "@/assets/logo.svg";
|
13 |
|
|
|
15 |
const [data, setData] = useState("");
|
16 |
const [error, setError] = useState("");
|
17 |
const [loading, setLoading] = useState(false);
|
18 |
+
const [loadingShare, setLoadingShare] = useState(false);
|
19 |
+
const [hfUser, setHfUser] = useState<string | undefined>(undefined);
|
20 |
+
const [quote, setQuote] = useState<string | undefined>(undefined);
|
21 |
|
22 |
const handleRoast = async (form: FormProps) => {
|
23 |
setError("");
|
|
|
32 |
if (res.error) {
|
33 |
setError(res.error);
|
34 |
} else {
|
35 |
+
setQuote(undefined);
|
36 |
+
setHfUser(form.username);
|
37 |
setData(res?.data);
|
38 |
}
|
39 |
|
40 |
setLoading(false);
|
41 |
};
|
42 |
|
43 |
+
const handleShare = async (form: ShareProps) => {
|
44 |
+
setLoadingShare(true);
|
45 |
+
const res = await share({ hf_user: form.hf_user, text: form.text });
|
46 |
+
if (res?.data) {
|
47 |
+
setQuote(res.data.id);
|
48 |
+
}
|
49 |
+
setLoadingShare(false);
|
50 |
+
};
|
51 |
+
|
52 |
return (
|
53 |
+
<>
|
54 |
<div className="max-w-2xl w-full border border-gray-200 bg-white rounded-3xl p-8 grid gap-8 shadow-xl shadow-black/5">
|
55 |
<header className="flex items-start max-lg:gap-1 lg:items-center justify-between max-lg:flex-col border-b border-zinc-200 pb-5">
|
56 |
<Image
|
|
|
75 |
<Form loading={loading} onSubmit={handleRoast} />
|
76 |
</div>
|
77 |
{data && (
|
78 |
+
<Quote data={data}>
|
79 |
+
{quote ? (
|
80 |
+
<CopyToClipboard id={quote} />
|
81 |
+
) : (
|
82 |
+
<button
|
83 |
+
className={classNames(
|
84 |
+
"bg-black rounded-full mt-4 px-4 py-2.5 text-sm font-medium text-white hover:bg-zinc-800 disabled:bg-zinc-300 disabled:text-zinc-500 disabled:cursor-not-allowed",
|
85 |
+
{
|
86 |
+
"animate-pulse": loadingShare,
|
87 |
+
}
|
88 |
+
)}
|
89 |
+
disabled={loadingShare}
|
90 |
+
onClick={() =>
|
91 |
+
hfUser && handleShare({ hf_user: hfUser, text: data })
|
|
|
92 |
}
|
93 |
+
>
|
94 |
+
{loadingShare ? "Creating a quote..." : "Share my roast!"}
|
95 |
+
</button>
|
96 |
+
)}
|
97 |
+
</Quote>
|
|
|
98 |
)}
|
99 |
+
</>
|
100 |
);
|
101 |
}
|
components/copy.tsx
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import classNames from "classnames";
|
2 |
+
import { CopyIcon } from "lucide-react";
|
3 |
+
import { useMemo, useState } from "react";
|
4 |
+
import { useCopyToClipboard, useUpdateEffect } from "react-use";
|
5 |
+
|
6 |
+
export const CopyToClipboard = ({ id }: { id: string }) => {
|
7 |
+
const [state, copyToClipboard] = useCopyToClipboard();
|
8 |
+
const [copied, setCopied] = useState(false);
|
9 |
+
|
10 |
+
const url = useMemo(() => {
|
11 |
+
return `https://huggingface.co/spaces/enzostvs/hugger-roaster/${id}`;
|
12 |
+
}, [id]);
|
13 |
+
|
14 |
+
const handleCopy = () => {
|
15 |
+
setCopied(true);
|
16 |
+
copyToClipboard(url);
|
17 |
+
setTimeout(() => {
|
18 |
+
setCopied(false);
|
19 |
+
}, 2000);
|
20 |
+
};
|
21 |
+
|
22 |
+
return (
|
23 |
+
<div className="w-full mt-5">
|
24 |
+
<p className="text-xs text-zinc-500">
|
25 |
+
Share this link with your friends so they can see your roast and try it
|
26 |
+
out themselves!
|
27 |
+
</p>
|
28 |
+
<div
|
29 |
+
className="bg-white mt-2 max-w-max rounded-md mr-2 border border-gray-200 text-sm px-3 py-2.5 relative ring-transparent text-zinc-600 hover:ring-blue-500/20 ring-[3px] flex items-center justify-center group"
|
30 |
+
onClick={handleCopy}
|
31 |
+
>
|
32 |
+
<div
|
33 |
+
className={classNames(
|
34 |
+
"bg-black/80 text-xs text-white px-2 py-1 rounded-md absolute left-0 top-0 -translate-y-1/2 transition-all duration-200",
|
35 |
+
{
|
36 |
+
"opacity-0 !translate-y-0": !copied,
|
37 |
+
}
|
38 |
+
)}
|
39 |
+
>
|
40 |
+
Copied!
|
41 |
+
</div>
|
42 |
+
<CopyIcon className="w-4 h-4 text-zinc-400 mr-2 group-hover:text-blue-500" />
|
43 |
+
<p className="flex-1">{url}</p>
|
44 |
+
</div>
|
45 |
+
</div>
|
46 |
+
);
|
47 |
+
};
|
components/quote.tsx
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const Quote = ({
|
2 |
+
data,
|
3 |
+
children,
|
4 |
+
}: {
|
5 |
+
data: string;
|
6 |
+
children?: React.ReactNode;
|
7 |
+
}) => {
|
8 |
+
return (
|
9 |
+
<div className="max-w-2xl w-full border border-gray-200 bg-white rounded-3xl p-8 shadow-xl shadow-black/5 relative z-[1] overflow-hidden">
|
10 |
+
<p className="text-[8rem] absolute bottom-0 translate-y-1/3 right-0 opacity-20 -z-[1]">
|
11 |
+
🧨
|
12 |
+
</p>
|
13 |
+
<p className="uppercase text-base tracking-wider font-semibold mb-2">
|
14 |
+
Roasting
|
15 |
+
</p>
|
16 |
+
<div className="text-lg text-gray-500 leading-relaxed container mx-auto text-pretty whitespace-break-spaces">
|
17 |
+
{data}
|
18 |
+
</div>
|
19 |
+
{children}
|
20 |
+
</div>
|
21 |
+
);
|
22 |
+
};
|
entrypoint.sh
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
npm run build && npx prisma generate && npx prisma migrate deploy && npx prisma db push && npm start
|
package-lock.json
CHANGED
@@ -9,10 +9,12 @@
|
|
9 |
"version": "0.1.0",
|
10 |
"dependencies": {
|
11 |
"@huggingface/inference": "^2.8.0",
|
|
|
12 |
"@xenova/transformers": "^2.17.2",
|
13 |
"classnames": "^2.5.1",
|
14 |
"lucide-react": "^0.436.0",
|
15 |
"next": "14.2.7",
|
|
|
16 |
"react": "^18",
|
17 |
"react-dom": "^18",
|
18 |
"react-use": "^17.5.1"
|
@@ -460,6 +462,63 @@
|
|
460 |
"node": ">=14"
|
461 |
}
|
462 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
463 |
"node_modules/@protobufjs/aspromise": {
|
464 |
"version": "1.1.2",
|
465 |
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
@@ -2463,7 +2522,6 @@
|
|
2463 |
"version": "2.3.3",
|
2464 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
2465 |
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
2466 |
-
"dev": true,
|
2467 |
"hasInstallScript": true,
|
2468 |
"optional": true,
|
2469 |
"os": [
|
@@ -4308,6 +4366,24 @@
|
|
4308 |
"node": ">= 0.8.0"
|
4309 |
}
|
4310 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4311 |
"node_modules/prop-types": {
|
4312 |
"version": "15.8.1",
|
4313 |
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
|
9 |
"version": "0.1.0",
|
10 |
"dependencies": {
|
11 |
"@huggingface/inference": "^2.8.0",
|
12 |
+
"@prisma/client": "^5.19.0",
|
13 |
"@xenova/transformers": "^2.17.2",
|
14 |
"classnames": "^2.5.1",
|
15 |
"lucide-react": "^0.436.0",
|
16 |
"next": "14.2.7",
|
17 |
+
"prisma": "^5.19.0",
|
18 |
"react": "^18",
|
19 |
"react-dom": "^18",
|
20 |
"react-use": "^17.5.1"
|
|
|
462 |
"node": ">=14"
|
463 |
}
|
464 |
},
|
465 |
+
"node_modules/@prisma/client": {
|
466 |
+
"version": "5.19.0",
|
467 |
+
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.19.0.tgz",
|
468 |
+
"integrity": "sha512-CzOpau+q1kEWQyoQMvlnXIHqPvwmWbh48xZ4n8KWbAql0p8PC0BIgSTYW5ncxXa4JSEff0tcoxSZB874wDstdg==",
|
469 |
+
"hasInstallScript": true,
|
470 |
+
"engines": {
|
471 |
+
"node": ">=16.13"
|
472 |
+
},
|
473 |
+
"peerDependencies": {
|
474 |
+
"prisma": "*"
|
475 |
+
},
|
476 |
+
"peerDependenciesMeta": {
|
477 |
+
"prisma": {
|
478 |
+
"optional": true
|
479 |
+
}
|
480 |
+
}
|
481 |
+
},
|
482 |
+
"node_modules/@prisma/debug": {
|
483 |
+
"version": "5.19.0",
|
484 |
+
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.19.0.tgz",
|
485 |
+
"integrity": "sha512-+b/G0ubAZlrS+JSiDhXnYV5DF/aTJ3pinktkiV/L4TtLRLZO6SVGyFELgxBsicCTWJ2ZMu5vEV/jTtYCdjFTRA=="
|
486 |
+
},
|
487 |
+
"node_modules/@prisma/engines": {
|
488 |
+
"version": "5.19.0",
|
489 |
+
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.19.0.tgz",
|
490 |
+
"integrity": "sha512-UtW+0m4HYoRSSR3LoDGKF3Ud4BSMWYlLEt4slTnuP1mI+vrV3zaDoiAPmejdAT76vCN5UqnWURbkXxf66nSylQ==",
|
491 |
+
"hasInstallScript": true,
|
492 |
+
"dependencies": {
|
493 |
+
"@prisma/debug": "5.19.0",
|
494 |
+
"@prisma/engines-version": "5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f",
|
495 |
+
"@prisma/fetch-engine": "5.19.0",
|
496 |
+
"@prisma/get-platform": "5.19.0"
|
497 |
+
}
|
498 |
+
},
|
499 |
+
"node_modules/@prisma/engines-version": {
|
500 |
+
"version": "5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f",
|
501 |
+
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f.tgz",
|
502 |
+
"integrity": "sha512-GimI9aZIFy/yvvR11KfXRn3pliFn1QAkdebVlsXlnoh5uk0YhLblVmeYiHfsu+wDA7BeKqYT4sFfzg8mutzuWw=="
|
503 |
+
},
|
504 |
+
"node_modules/@prisma/fetch-engine": {
|
505 |
+
"version": "5.19.0",
|
506 |
+
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.19.0.tgz",
|
507 |
+
"integrity": "sha512-oOiPNtmJX0cP/ebu7BBEouJvCw8T84/MFD/Hf2zlqjxkK4ojl38bB9i9J5LAxotL6WlYVThKdxc7HqoWnPOhqQ==",
|
508 |
+
"dependencies": {
|
509 |
+
"@prisma/debug": "5.19.0",
|
510 |
+
"@prisma/engines-version": "5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f",
|
511 |
+
"@prisma/get-platform": "5.19.0"
|
512 |
+
}
|
513 |
+
},
|
514 |
+
"node_modules/@prisma/get-platform": {
|
515 |
+
"version": "5.19.0",
|
516 |
+
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.19.0.tgz",
|
517 |
+
"integrity": "sha512-s9DWkZKnuP4Y8uy6yZfvqQ/9X3/+2KYf3IZUVZz5OstJdGBJrBlbmIuMl81917wp5TuK/1k2TpHNCEdpYLPKmg==",
|
518 |
+
"dependencies": {
|
519 |
+
"@prisma/debug": "5.19.0"
|
520 |
+
}
|
521 |
+
},
|
522 |
"node_modules/@protobufjs/aspromise": {
|
523 |
"version": "1.1.2",
|
524 |
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
|
|
2522 |
"version": "2.3.3",
|
2523 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
2524 |
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
|
|
2525 |
"hasInstallScript": true,
|
2526 |
"optional": true,
|
2527 |
"os": [
|
|
|
4366 |
"node": ">= 0.8.0"
|
4367 |
}
|
4368 |
},
|
4369 |
+
"node_modules/prisma": {
|
4370 |
+
"version": "5.19.0",
|
4371 |
+
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.19.0.tgz",
|
4372 |
+
"integrity": "sha512-Pu7lUKpVyTx8cVwM26dYh8NdvMOkMnJXzE8L6cikFuR4JwyMU5NKofQkWyxJKlTT4fNjmcnibTvklV8oVMrn+g==",
|
4373 |
+
"hasInstallScript": true,
|
4374 |
+
"dependencies": {
|
4375 |
+
"@prisma/engines": "5.19.0"
|
4376 |
+
},
|
4377 |
+
"bin": {
|
4378 |
+
"prisma": "build/index.js"
|
4379 |
+
},
|
4380 |
+
"engines": {
|
4381 |
+
"node": ">=16.13"
|
4382 |
+
},
|
4383 |
+
"optionalDependencies": {
|
4384 |
+
"fsevents": "2.3.3"
|
4385 |
+
}
|
4386 |
+
},
|
4387 |
"node_modules/prop-types": {
|
4388 |
"version": "15.8.1",
|
4389 |
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
package.json
CHANGED
@@ -10,10 +10,12 @@
|
|
10 |
},
|
11 |
"dependencies": {
|
12 |
"@huggingface/inference": "^2.8.0",
|
|
|
13 |
"@xenova/transformers": "^2.17.2",
|
14 |
"classnames": "^2.5.1",
|
15 |
"lucide-react": "^0.436.0",
|
16 |
"next": "14.2.7",
|
|
|
17 |
"react": "^18",
|
18 |
"react-dom": "^18",
|
19 |
"react-use": "^17.5.1"
|
|
|
10 |
},
|
11 |
"dependencies": {
|
12 |
"@huggingface/inference": "^2.8.0",
|
13 |
+
"@prisma/client": "^5.19.0",
|
14 |
"@xenova/transformers": "^2.17.2",
|
15 |
"classnames": "^2.5.1",
|
16 |
"lucide-react": "^0.436.0",
|
17 |
"next": "14.2.7",
|
18 |
+
"prisma": "^5.19.0",
|
19 |
"react": "^18",
|
20 |
"react-dom": "^18",
|
21 |
"react-use": "^17.5.1"
|
prisma/dev.db
ADDED
Binary file (20.5 kB). View file
|
|
prisma/schema.prisma
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
generator client {
|
2 |
+
provider = "prisma-client-js"
|
3 |
+
}
|
4 |
+
|
5 |
+
datasource db {
|
6 |
+
provider = "sqlite"
|
7 |
+
url = env("DATABASE_URL")
|
8 |
+
}
|
9 |
+
|
10 |
+
model Quote {
|
11 |
+
id String @id @default(cuid())
|
12 |
+
hf_user String
|
13 |
+
text String
|
14 |
+
}
|
utils/prisma.ts
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { PrismaClient } from '@prisma/client'
|
2 |
+
|
3 |
+
const prisma = new PrismaClient()
|
4 |
+
export default prisma
|
utils/roast copy.ts
DELETED
@@ -1,55 +0,0 @@
|
|
1 |
-
const formatSpacesInfos = (spaces: any) => {
|
2 |
-
const texts: string[] = []
|
3 |
-
spaces.map((space: any) => {
|
4 |
-
let text = `${space.cardData?.title} has ${space.likes} likes and has been updated ${space.lastModified} ago.`
|
5 |
-
if (space.cardData?.short_description) {
|
6 |
-
text += `The space description is: ${space.cardData?.short_description}`
|
7 |
-
}
|
8 |
-
texts.push(text)
|
9 |
-
})
|
10 |
-
|
11 |
-
return texts.join("\n")
|
12 |
-
}
|
13 |
-
|
14 |
-
const formatModelsInfos = (models: any) => {
|
15 |
-
const texts: string[] = []
|
16 |
-
models.map((model: any) => {
|
17 |
-
let text = `${model.id?.split("/")[1]} has ${model.likes} likes and ${model.downloads} downloads.`
|
18 |
-
if (model.gating) {
|
19 |
-
text += `You should ask for access to this model.`
|
20 |
-
} else {
|
21 |
-
text += `This model is public.`
|
22 |
-
}
|
23 |
-
text += `This model is about ${model.pipeline_tag}`
|
24 |
-
|
25 |
-
texts.push(text)
|
26 |
-
})
|
27 |
-
|
28 |
-
return texts.join("\n")
|
29 |
-
}
|
30 |
-
|
31 |
-
const formatUserInfos = (user: any, countFollowing: number, countFollowers: number, spacesLikes: number, modelsLikes: number, spaces: any, models: any) => {
|
32 |
-
return `
|
33 |
-
The user ${user.fullname} has ${countFollowers} followers and is following ${countFollowing} users.
|
34 |
-
He is part of ${user.orgs?.length ?? 0} organizations.
|
35 |
-
He is owner of ${spaces?.length ?? 0} spaces and has ${models?.length ?? 0} models.
|
36 |
-
He already liked ${user.likes?.length ?? 0} models/spaces/datasets. This user is ${user.isPro ? "pro" : "not pro"}.
|
37 |
-
He has ${spacesLikes} likes on his spaces and ${modelsLikes} likes on his models.
|
38 |
-
`
|
39 |
-
}
|
40 |
-
|
41 |
-
export const formatInformations = (
|
42 |
-
user: any,
|
43 |
-
countFollowing: number,
|
44 |
-
countFollowers: number,
|
45 |
-
spaces: any,
|
46 |
-
models: any,
|
47 |
-
spacesLikes: number,
|
48 |
-
modelsLikes: number
|
49 |
-
) => {
|
50 |
-
const userInfos = formatUserInfos(user, countFollowing, countFollowers, spacesLikes, modelsLikes, spaces, models)
|
51 |
-
const spacesInfos = formatSpacesInfos(spaces.slice(0, 6))
|
52 |
-
const modelsInfos = formatModelsInfos(models.slice(0, 6))
|
53 |
-
|
54 |
-
return `${userInfos}\n\n${spacesInfos}\n\n${modelsInfos}`
|
55 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|