enzostvs HF staff commited on
Commit
8fce765
β€’
1 Parent(s): 09df05f

refacto whole models system

Browse files
package-lock.json CHANGED
@@ -19,6 +19,7 @@
19
  "desm": "^1.3.1",
20
  "express": "^4.18.2",
21
  "js-cookie": "^3.0.5",
 
22
  "svelte-infinite-scroll": "^2.0.1"
23
  },
24
  "devDependencies": {
@@ -5250,6 +5251,14 @@
5250
  "mkdirp": "bin/cmd.js"
5251
  }
5252
  },
 
 
 
 
 
 
 
 
5253
  "node_modules/mri": {
5254
  "version": "1.2.0",
5255
  "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
 
19
  "desm": "^1.3.1",
20
  "express": "^4.18.2",
21
  "js-cookie": "^3.0.5",
22
+ "moment": "^2.30.1",
23
  "svelte-infinite-scroll": "^2.0.1"
24
  },
25
  "devDependencies": {
 
5251
  "mkdirp": "bin/cmd.js"
5252
  }
5253
  },
5254
+ "node_modules/moment": {
5255
+ "version": "2.30.1",
5256
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
5257
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
5258
+ "engines": {
5259
+ "node": "*"
5260
+ }
5261
+ },
5262
  "node_modules/mri": {
5263
  "version": "1.2.0",
5264
  "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
package.json CHANGED
@@ -52,6 +52,7 @@
52
  "desm": "^1.3.1",
53
  "express": "^4.18.2",
54
  "js-cookie": "^3.0.5",
 
55
  "svelte-infinite-scroll": "^2.0.1"
56
  }
57
  }
 
52
  "desm": "^1.3.1",
53
  "express": "^4.18.2",
54
  "js-cookie": "^3.0.5",
55
+ "moment": "^2.30.1",
56
  "svelte-infinite-scroll": "^2.0.1"
57
  }
58
  }
prisma/schema.prisma CHANGED
@@ -13,7 +13,6 @@ datasource db {
13
  model Model {
14
  id String @id
15
  createdAt DateTime @default(now())
16
- title String
17
  image String
18
  likes Int?
19
  downloads Int?
 
13
  model Model {
14
  id String @id
15
  createdAt DateTime @default(now())
 
16
  image String
17
  likes Int?
18
  downloads Int?
src/lib/components/Button.svelte CHANGED
@@ -3,7 +3,7 @@
3
  import Icon from "@iconify/svelte";
4
  import Loading from './Loading.svelte';
5
 
6
- export let theme: "light" | "dark" | "blue" | "pink" = "light";
7
  export let size: "md" | "lg" = "md";
8
  export let href: string | undefined = undefined;
9
  export let icon: string | undefined = undefined;
@@ -73,6 +73,9 @@
73
  &.dark {
74
  @apply bg-neutral-900 border-neutral-800 text-neutral-300;
75
  }
 
 
 
76
  &:hover {
77
  @apply brightness-125
78
  }
 
3
  import Icon from "@iconify/svelte";
4
  import Loading from './Loading.svelte';
5
 
6
+ export let theme: "light" | "dark" | "blue" | "pink" | "red" = "light";
7
  export let size: "md" | "lg" = "md";
8
  export let href: string | undefined = undefined;
9
  export let icon: string | undefined = undefined;
 
73
  &.dark {
74
  @apply bg-neutral-900 border-neutral-800 text-neutral-300;
75
  }
76
+ &.red {
77
+ @apply bg-red-500 text-white border-red-500;
78
+ }
79
  &:hover {
80
  @apply brightness-125
81
  }
src/lib/components/community/Card.svelte CHANGED
@@ -45,7 +45,7 @@
45
  on:click={handleClick}
46
  >
47
  <div class="w-full h-full absolute top-0 left-0 -z-[1] rounded-xl overflow-hidden" class:!brightness-50={loading}>
48
- <img class="w-full h-full bg-center bg-cover transition-all duration-200 group-hover:scale-110" src="/api/images/{card.image}" alt="{card.prompt}" />
49
  <div class="bg-gradient-to-b from-transparent via-black/50 to-black/70 absolute h-[100px] bottom-0 left-0 w-full"></div>
50
  </div>
51
  <div class="group-hover:opacity-100 opacity-0 translate-y-full group-hover:translate-y-0 transition-all duration-200 flex flex-col gap-4 w-full">
 
45
  on:click={handleClick}
46
  >
47
  <div class="w-full h-full absolute top-0 left-0 -z-[1] rounded-xl overflow-hidden" class:!brightness-50={loading}>
48
+ <img class="w-full h-full bg-center bg-cover transition-all duration-200 group-hover:scale-110 object-cover" src="/api/images/{card.image}" alt="{card.prompt}" />
49
  <div class="bg-gradient-to-b from-transparent via-black/50 to-black/70 absolute h-[100px] bottom-0 left-0 w-full"></div>
50
  </div>
51
  <div class="group-hover:opacity-100 opacity-0 translate-y-full group-hover:translate-y-0 transition-all duration-200 flex flex-col gap-4 w-full">
src/lib/components/models/Card.svelte CHANGED
@@ -1,35 +1,64 @@
1
  <script lang="ts">
2
- import { modelStore } from "$lib/stores/use-model";
3
  import type { ModelCard } from "$lib/type";
4
  import Icon from "@iconify/svelte";
5
  import { goto } from "$app/navigation";
6
  import { page } from "$app/stores";
 
 
7
 
8
  export let card: ModelCard;
9
 
 
 
10
  const handleClick = async () => {
11
- // const request = await fetch(`/api/models/${card?.id?.replace("/", "@")}?full=true`);
12
- // const { model } = await request.json();
13
- // modelStore.set({
14
- // model,
15
- // open: true
16
- // });
17
  $page.url.searchParams.set('model', card?.id);
18
  goto(`?${$page.url.searchParams.toString()}`);
19
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  </script>
21
 
22
  <!-- svelte-ignore a11y-no-static-element-interactions -->
23
  <!-- svelte-ignore a11y-click-events-have-key-events -->
 
24
  <div
25
  class="w-full cursor-pointer group bg-neutral-900 rounded-xl relative flex items-start justify-between flex-col p-3 border border-neutral-800 transition-all duration-200 brightness-90 hover:brightness-100 z-[1]"
26
  on:click={handleClick}
27
  >
28
  <div class="w-full h-[350px] relative z-[1] mb-3 overflow-hidden">
29
- <img src="{card.image}" class="w-full h-full bg-center bg-cover rounded-lg object-cover object-center bg-neutral-800" alt="{card?.title}" />
 
 
 
 
 
 
 
 
 
30
  </div>
31
  <div class="flex items-center justify-between w-full gap-4 py-1">
32
- <p class="text-white font-semibold text-base mb-1 truncate">{card?.title ?? card?.id}</p>
33
  <div class="flex items-center justify-end gap-3">
34
  <div class="text-white text-sm flex items-center justify-end gap-1.5">
35
  <Icon icon="solar:heart-bold" class="w-5 h-5 text-red-500" />
@@ -41,4 +70,5 @@
41
  </div>
42
  </div>
43
  </div>
44
- </div>
 
 
1
  <script lang="ts">
 
2
  import type { ModelCard } from "$lib/type";
3
  import Icon from "@iconify/svelte";
4
  import { goto } from "$app/navigation";
5
  import { page } from "$app/stores";
6
+ import Button from "$lib/components/Button.svelte";
7
+ import { success } from "$lib/utils/toaster";
8
 
9
  export let card: ModelCard;
10
 
11
+ let visible = true;
12
+
13
  const handleClick = async () => {
 
 
 
 
 
 
14
  $page.url.searchParams.set('model', card?.id);
15
  goto(`?${$page.url.searchParams.toString()}`);
16
  };
17
+
18
+ const publish = async () => {
19
+ const request = await fetch(`/api/models/${card.id.replace("/", "@")}`, {
20
+ method: "POST",
21
+ });
22
+ const response = await request.json();
23
+ if (response.success) {
24
+ card.isPublic = true;
25
+ success("Model published successfully!");
26
+ }
27
+ };
28
+
29
+ const remove = async () => {
30
+ const request = await fetch(`/api/models/${card.id.replace("/", "@")}`, {
31
+ method: "DELETE",
32
+ });
33
+ const response = await request.json();
34
+ if (response.success) {
35
+ visible = false;
36
+ success("Model deleted successfully!");
37
+ }
38
+ };
39
  </script>
40
 
41
  <!-- svelte-ignore a11y-no-static-element-interactions -->
42
  <!-- svelte-ignore a11y-click-events-have-key-events -->
43
+ {#if visible}
44
  <div
45
  class="w-full cursor-pointer group bg-neutral-900 rounded-xl relative flex items-start justify-between flex-col p-3 border border-neutral-800 transition-all duration-200 brightness-90 hover:brightness-100 z-[1]"
46
  on:click={handleClick}
47
  >
48
  <div class="w-full h-[350px] relative z-[1] mb-3 overflow-hidden">
49
+ <img src="{card.image}" class="w-full h-full bg-center bg-cover rounded-lg object-cover object-center bg-neutral-800" alt="{card?.id}" />
50
+ {#if !card.isPublic}
51
+ <div
52
+ class="absolute flex items-center justify-between bottom-0 left-0 w-full p-5 bg-neutral-900/30"
53
+ on:click={e => e.stopPropagation()}
54
+ >
55
+ <Button theme="red" onClick={remove}>Delete</Button>
56
+ <Button theme="blue" icon="icon-park-solid:check-one" onClick={publish}>Publish</Button>
57
+ </div>
58
+ {/if}
59
  </div>
60
  <div class="flex items-center justify-between w-full gap-4 py-1">
61
+ <p class="text-white font-semibold text-base mb-1 truncate">{card?.id}</p>
62
  <div class="flex items-center justify-end gap-3">
63
  <div class="text-white text-sm flex items-center justify-end gap-1.5">
64
  <Icon icon="solar:heart-bold" class="w-5 h-5 text-red-500" />
 
70
  </div>
71
  </div>
72
  </div>
73
+ </div>
74
+ {/if}
src/lib/components/models/autocomplete/Autocomplete.svelte CHANGED
@@ -51,8 +51,8 @@
51
  {#if value}
52
  <div class="flex items-center justify-between gap-4">
53
  <div class="flex items-center justify-start gap-3">
54
- <img src={value.image} alt={value.title} class="w-6 h-6 rounded-lg object-cover" />
55
- <p class="text-neutral-200 text-base font-medium">{value.title}</p>
56
  </div>
57
  <button on:click={() => onChange(null)}>
58
  <Icon icon="maki:cross" class="w-4 h-4 text-neutral-500 transition-all duration-200 cursor-pointer" />
 
51
  {#if value}
52
  <div class="flex items-center justify-between gap-4">
53
  <div class="flex items-center justify-start gap-3">
54
+ <img src={value.image} alt={value.id} class="w-6 h-6 rounded-lg object-cover" />
55
+ <p class="text-neutral-200 text-base font-medium">{value.id}</p>
56
  </div>
57
  <button on:click={() => onChange(null)}>
58
  <Icon icon="maki:cross" class="w-4 h-4 text-neutral-500 transition-all duration-200 cursor-pointer" />
src/lib/components/models/autocomplete/Item.svelte CHANGED
@@ -10,9 +10,9 @@
10
  class="flex items-center justify-start gap-4 px-2 py-2.5 hover:bg-neutral-800/60 transition-all duration-200 rounded-lg cursor-pointer w-full text-left"
11
  on:click={onClick}
12
  >
13
- <img src={model.image} alt={model.title} class="w-14 h-14 rounded-lg object-cover" />
14
  <div>
15
- <p class="text-neutral-200 text-base font-medium">{model.title}</p>
16
  <p class="text-neutral-400 text-sm">{model.id}</p>
17
  </div>
18
  </button>
 
10
  class="flex items-center justify-start gap-4 px-2 py-2.5 hover:bg-neutral-800/60 transition-all duration-200 rounded-lg cursor-pointer w-full text-left"
11
  on:click={onClick}
12
  >
13
+ <img src={model.image} alt={model.id} class="w-14 h-14 rounded-lg object-cover" />
14
  <div>
15
+ <p class="text-neutral-200 text-base font-medium">{model.id}</p>
16
  <p class="text-neutral-400 text-sm">{model.id}</p>
17
  </div>
18
  </button>
src/lib/components/models/drawer/Drawer.svelte CHANGED
@@ -56,7 +56,7 @@
56
  <img src={model?.image} class="lg:w-16 lg:h-16 w-12 h-12 rounded-xl bg-neutral-800 object-cover" alt={model?.id} />
57
  <div>
58
  <p class="text-white font-semibold text-lg lg:text-2xl mb-1 truncate">
59
- {model?.title ?? model?.id}
60
  </p>
61
  <a href="https://huggingface.co/{model?.id}" target="_blank" class="text-neutral-400 underline hover:text-neutral-300 flex items-center justify-start gap-1">
62
  <Icon icon="iconamoon:link-external-fill" class="w-4 h-4" />
@@ -75,7 +75,8 @@
75
  {model?.likes ?? 0}
76
  </div>
77
  <a
78
- href="https://huggingface.co/{model?.id}/tree/main"
 
79
  class="bg-blue-500 bg-opacity-20 border border-blue-500 hover:bg-opacity-60 transition-all duration-200 md:px-4 md:py-2 px-3 py-1.5 rounded-full text-neutral-100 flex items-center justify-center gap-1 md:gap-2 font-medium text-sm md:text-base"
80
  >
81
  <Icon icon="solar:download-square-bold" class="lg:w-5 lg:h-5 w-4 h-4 text-blue-500" />
 
56
  <img src={model?.image} class="lg:w-16 lg:h-16 w-12 h-12 rounded-xl bg-neutral-800 object-cover" alt={model?.id} />
57
  <div>
58
  <p class="text-white font-semibold text-lg lg:text-2xl mb-1 truncate">
59
+ {model?.id}
60
  </p>
61
  <a href="https://huggingface.co/{model?.id}" target="_blank" class="text-neutral-400 underline hover:text-neutral-300 flex items-center justify-start gap-1">
62
  <Icon icon="iconamoon:link-external-fill" class="w-4 h-4" />
 
75
  {model?.likes ?? 0}
76
  </div>
77
  <a
78
+ href="https://huggingface.co/{model?.id}/tree/main"
79
+ target="_blank"
80
  class="bg-blue-500 bg-opacity-20 border border-blue-500 hover:bg-opacity-60 transition-all duration-200 md:px-4 md:py-2 px-3 py-1.5 rounded-full text-neutral-100 flex items-center justify-center gap-1 md:gap-2 font-medium text-sm md:text-base"
81
  >
82
  <Icon icon="solar:download-square-bold" class="lg:w-5 lg:h-5 w-4 h-4 text-blue-500" />
src/lib/components/sidebar/Sidebar.svelte CHANGED
@@ -26,7 +26,6 @@
26
  window.location.href = "/";
27
  }
28
 
29
- // sveltekit close sidebar on route change
30
  if (browser) {
31
  page.subscribe((value) => {
32
  if (isOpen) handleClick();
@@ -69,7 +68,12 @@
69
  <div class="flex items-center justify-start gap-4">
70
  <img src={user.picture} alt="User avatar" class="w-10 h-10 rounded-full border-2 border-white inline-block" />
71
  <div class="w-full text-left">
72
- <p class="text-lg font-semibold">{user.name}</p>
 
 
 
 
 
73
  <p class="text-sm leading-none text-neutral-400">{user.preferred_username}</p>
74
  </div>
75
  </div>
 
26
  window.location.href = "/";
27
  }
28
 
 
29
  if (browser) {
30
  page.subscribe((value) => {
31
  if (isOpen) handleClick();
 
68
  <div class="flex items-center justify-start gap-4">
69
  <img src={user.picture} alt="User avatar" class="w-10 h-10 rounded-full border-2 border-white inline-block" />
70
  <div class="w-full text-left">
71
+ <p class="text-lg font-semibold">
72
+ {user.name}
73
+ {#if user?.is_admin}
74
+ <span class="text-yellow-400 bg-yellow-500 bg-opacity-20 rounded-lg px-2 py-1 text-xs font-semibold ml-1">HF Staff</span>
75
+ {/if}
76
+ </p>
77
  <p class="text-sm leading-none text-neutral-400">{user.preferred_username}</p>
78
  </div>
79
  </div>
src/lib/stores/use-user.ts CHANGED
@@ -6,16 +6,4 @@ export const userStore = writable<any>(null);
6
 
7
  export const openWindowLogin = async () => {
8
  window.open("/api/auth/login", "_blank");
9
- };
10
-
11
- // export const loginFromCode = async (code: string) => {
12
- // const request = await fetch(`/api/auth`, {
13
- // method: "POST",
14
- // body: JSON.stringify({ code }),
15
- // });
16
- // const { ok, token } = await request.json();
17
- // if (ok) {
18
- // cookies.set("hf_access_token", token, { expires: 1, domain: process.env.SPACE_HOST });
19
- // window.location.reload();
20
- // }
21
- // };
 
6
 
7
  export const openWindowLogin = async () => {
8
  window.open("/api/auth/login", "_blank");
9
+ };
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/type.ts CHANGED
@@ -16,12 +16,12 @@ export interface CommunityCard {
16
  }
17
 
18
  export interface ModelCard {
19
- title: string,
20
  id: string,
21
  likes: number,
22
  downloads: number,
23
  image: string,
24
  instance_prompt?: string,
 
25
  gallery?: CommunityCard[],
26
  comments?: CommentType[],
27
  infos?: {
 
16
  }
17
 
18
  export interface ModelCard {
 
19
  id: string,
20
  likes: number,
21
  downloads: number,
22
  image: string,
23
  instance_prompt?: string,
24
+ isPublic: boolean,
25
  gallery?: CommunityCard[],
26
  comments?: CommentType[],
27
  infos?: {
src/routes/+page.ts CHANGED
@@ -3,7 +3,6 @@ import { modelStore } from "$lib/stores/use-model";
3
  export async function load({ fetch, url }) {
4
  const model_param = url.searchParams.get("model")
5
 
6
-
7
  if (model_param) {
8
  const model_request = await fetch(`/api/models/${model_param?.replace("/", "@")}?full=true`, {
9
  method: "GET",
 
3
  export async function load({ fetch, url }) {
4
  const model_param = url.searchParams.get("model")
5
 
 
6
  if (model_param) {
7
  const model_request = await fetch(`/api/models/${model_param?.replace("/", "@")}?full=true`, {
8
  method: "GET",
src/routes/api/@me/+server.ts CHANGED
@@ -1,5 +1,6 @@
1
  import { json, type RequestEvent } from '@sveltejs/kit';
2
  import prisma from '$lib/prisma';
 
3
 
4
  /** @type {import('./$types').RequestHandler} */
5
 
@@ -60,6 +61,9 @@ export async function GET(request : RequestEvent) {
60
  }
61
 
62
  return json({
63
- user
 
 
 
64
  })
65
  }
 
1
  import { json, type RequestEvent } from '@sveltejs/kit';
2
  import prisma from '$lib/prisma';
3
+ import { env } from '$env/dynamic/private';
4
 
5
  /** @type {import('./$types').RequestHandler} */
6
 
 
61
  }
62
 
63
  return json({
64
+ user: {
65
+ ...user,
66
+ is_admin: env.SECRET_HF_ADMIN.includes(user.sub)
67
+ }
68
  })
69
  }
src/routes/api/models/+server.ts CHANGED
@@ -1,9 +1,22 @@
1
  import { json, type RequestEvent } from '@sveltejs/kit';
2
  import prisma from '$lib/prisma';
 
 
 
3
 
4
  /** @type {import('./$types').RequestHandler} */
5
 
6
  export async function GET(request : RequestEvent) {
 
 
 
 
 
 
 
 
 
 
7
  const page = parseInt(request.url.searchParams.get('page') || '0')
8
  const filter = request.url.searchParams.get('filter') || 'hotest'
9
  const search = request.url.searchParams.get('search') || ''
@@ -11,9 +24,8 @@ export async function GET(request : RequestEvent) {
11
 
12
  const cards = await prisma.model.findMany({
13
  where: {
14
- isPublic: true,
15
  OR: [
16
- { title: { contains: search } },
17
  { id: { contains: search } },
18
  ]
19
  },
@@ -26,9 +38,8 @@ export async function GET(request : RequestEvent) {
26
 
27
  const total_reposId = await prisma.model.count({
28
  where: {
29
- isPublic: true,
30
  OR: [
31
- { title: { contains: search } },
32
  { id: { contains: search } },
33
  ]
34
  },
 
1
  import { json, type RequestEvent } from '@sveltejs/kit';
2
  import prisma from '$lib/prisma';
3
+ import { env } from '$env/dynamic/private';
4
+
5
+ import { tokenIsAvailable } from '$lib/utils';
6
 
7
  /** @type {import('./$types').RequestHandler} */
8
 
9
  export async function GET(request : RequestEvent) {
10
+ const token = request.cookies.get('hf_access_token')
11
+ let IS_ADMIN = !!token
12
+
13
+ if (token) {
14
+ const user = await tokenIsAvailable(token)
15
+ if (user) {
16
+ IS_ADMIN = env.SECRET_HF_ADMIN.includes(user.sub)
17
+ }
18
+ }
19
+
20
  const page = parseInt(request.url.searchParams.get('page') || '0')
21
  const filter = request.url.searchParams.get('filter') || 'hotest'
22
  const search = request.url.searchParams.get('search') || ''
 
24
 
25
  const cards = await prisma.model.findMany({
26
  where: {
27
+ ...(IS_ADMIN ? {} : { isPublic: true }),
28
  OR: [
 
29
  { id: { contains: search } },
30
  ]
31
  },
 
38
 
39
  const total_reposId = await prisma.model.count({
40
  where: {
41
+ ...(IS_ADMIN ? {} : { isPublic: true }),
42
  OR: [
 
43
  { id: { contains: search } },
44
  ]
45
  },
src/routes/api/models/[id]/+server.ts CHANGED
@@ -1,6 +1,7 @@
1
  import { json, type RequestEvent } from '@sveltejs/kit';
2
  import { env } from '$env/dynamic/private'
3
  import prisma from '$lib/prisma';
 
4
 
5
  /** @type {import('./$types').RequestHandler} */
6
 
@@ -18,7 +19,6 @@ export async function GET({ url, params } : RequestEvent) {
18
  likes: true,
19
  downloads: true,
20
  image: true,
21
- title: true,
22
  instance_prompt: true,
23
  gallery: {
24
  select: {
@@ -54,7 +54,6 @@ export async function GET({ url, params } : RequestEvent) {
54
  } : {
55
  instance_prompt: true,
56
  image: true,
57
- title: true,
58
  id: true,
59
  }
60
  })
@@ -90,3 +89,98 @@ export async function GET({ url, params } : RequestEvent) {
90
  }
91
  })
92
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { json, type RequestEvent } from '@sveltejs/kit';
2
  import { env } from '$env/dynamic/private'
3
  import prisma from '$lib/prisma';
4
+ import { tokenIsAvailable } from '$lib/utils';
5
 
6
  /** @type {import('./$types').RequestHandler} */
7
 
 
19
  likes: true,
20
  downloads: true,
21
  image: true,
 
22
  instance_prompt: true,
23
  gallery: {
24
  select: {
 
54
  } : {
55
  instance_prompt: true,
56
  image: true,
 
57
  id: true,
58
  }
59
  })
 
89
  }
90
  })
91
  }
92
+
93
+ export async function POST({ params, cookies } : RequestEvent) {
94
+ const token = cookies.get('hf_access_token')
95
+ if (!token) {
96
+ return json({
97
+ error: {
98
+ token: "Token is required"
99
+ }
100
+ }, { status: 401 })
101
+ }
102
+
103
+ const user = await tokenIsAvailable(token)
104
+ if (!user || !env.SECRET_HF_ADMIN.includes(user.sub)) {
105
+ return json({
106
+ error: {
107
+ token: "Wrong castle fam :^)"
108
+ }
109
+ }, { status: 401 })
110
+ }
111
+
112
+ const id = params.id?.replace("@", "/")
113
+
114
+ const model = await prisma.model.findFirst({
115
+ where: {
116
+ id,
117
+ }
118
+ })
119
+
120
+ if (!model) {
121
+ return json({
122
+ error: {
123
+ token: "Model not found"
124
+ }
125
+ }, { status: 404 })
126
+ }
127
+
128
+ await prisma.model.update({
129
+ where: {
130
+ id,
131
+ },
132
+ data: {
133
+ isPublic: true,
134
+ }
135
+ })
136
+
137
+ return json({
138
+ success: true
139
+ })
140
+ }
141
+
142
+ export async function DELETE({ params, cookies } : RequestEvent) {
143
+ const token = cookies.get('hf_access_token')
144
+ if (!token) {
145
+ return json({
146
+ error: {
147
+ token: "Token is required"
148
+ }
149
+ }, { status: 401 })
150
+ }
151
+
152
+ const user = await tokenIsAvailable(token)
153
+ if (!user || !env.SECRET_HF_ADMIN.includes(user.sub)) {
154
+ return json({
155
+ error: {
156
+ token: "Wrong castle fam :^)"
157
+ }
158
+ }, { status: 401 })
159
+ }
160
+
161
+ const id = params.id?.replace("@", "/")
162
+
163
+ const model = await prisma.model.findFirst({
164
+ where: {
165
+ id,
166
+ }
167
+ })
168
+
169
+ if (!model) {
170
+ return json({
171
+ error: {
172
+ token: "Model not found"
173
+ }
174
+ }, { status: 404 })
175
+ }
176
+
177
+ await prisma.model.delete({
178
+ where: {
179
+ id,
180
+ }
181
+ })
182
+
183
+ return json({
184
+ success: true
185
+ })
186
+ }
src/routes/api/models/submit/+server.ts CHANGED
@@ -56,7 +56,6 @@ export async function POST({ request, fetch, cookies }) {
56
  data: {
57
  id: model.id,
58
  image: model.image,
59
- title: model.title,
60
  likes: data.likes,
61
  downloads: data.downloads,
62
  isPublic: false,
 
56
  data: {
57
  id: model.id,
58
  image: model.image,
 
59
  likes: data.likes,
60
  downloads: data.downloads,
61
  isPublic: false,
src/routes/api/{bulk-create-models β†’ scrap-models}/+server.ts RENAMED
@@ -1,6 +1,7 @@
1
  /** @type {import('./$types').RequestHandler} */
2
 
3
  import { json } from '@sveltejs/kit';
 
4
  import prisma from '$lib/prisma';
5
  import { env } from '$env/dynamic/private'
6
 
@@ -13,39 +14,48 @@ export async function POST({ request }) {
13
  }, { status: 401 });
14
  }
15
 
16
- const { models } = await request.json();
 
17
 
18
- const cards = await Promise.all(models.map(async (model: Record<string, string>) => {
19
- const res = await fetch(`https://huggingface.co/api/models/${model.repo}`, {
20
- headers: {
21
- "Authorization": `Bearer ${env.SECRET_HF_TOKEN}`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
23
- })
24
- const data = await res.json();
25
- const mergedData = {
26
- image: model.image,
27
- id: model.repo,
28
- title: model.title,
29
- likes: data.likes,
30
- instance_prompt: data?.cardData?.instance_prompt ?? null,
31
- downloads: data.downloads,
32
  }
33
- return mergedData
34
- }
35
- ))
36
 
37
- for (const model of cards) {
38
  await prisma.model.create({
39
  data: {
40
  id: model.id,
41
  image: model.image,
42
- title: model.title,
43
  likes: model.likes,
44
- instance_prompt: model.instance_prompt,
45
  downloads: model.downloads,
46
- isPublic: true,
47
  }
48
- })
 
 
49
  }
50
 
51
  const total_items = await prisma.model.count()
 
1
  /** @type {import('./$types').RequestHandler} */
2
 
3
  import { json } from '@sveltejs/kit';
4
+ // import moment from 'moment';
5
  import prisma from '$lib/prisma';
6
  import { env } from '$env/dynamic/private'
7
 
 
14
  }, { status: 401 });
15
  }
16
 
17
+ const response = await fetch(`https://huggingface.co/api/models?limit=5000&filter=lora%2Cdiffusers&sort=createdAt&full=true&config=true`)
18
+ const responseData = await response.json();
19
 
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ let index = 0;
22
+ for (const model of responseData) {
23
+ if (index % 50 === 0) {
24
+ await new Promise(resolve => setTimeout(resolve, 4000));
25
+ }
26
+
27
+ const hasReadme = model?.siblings?.find((sibling: Record<string, string>) => sibling?.rfilename === "README.md")
28
+ if (hasReadme) {
29
+ const readmeRes = await fetch(`https://huggingface.co/${model.id}/raw/main/README.md`)
30
+ const readme = await readmeRes.text().catch(() => null)
31
+ if (!readme) {
32
+ continue
33
+ }
34
+ const imageRegex = /!\[.*\]\((.*)\)/
35
+ const image = readme.match(imageRegex)?.[1]
36
+
37
+ if (!image) {
38
+ continue
39
  }
40
+ if (image.startsWith("http")) model.image = image
41
+ else if (image.includes(model.id)) {
42
+ const path = image.split("/")
43
+ model.image = `https://huggingface.co/${model.id}/raw/main/${path[path.length - 1]}`
44
+ }
45
+ else model.image = `https://huggingface.co/${model.id}/raw/main/${image.replace("./", "")}`
 
 
 
46
  }
 
 
 
47
 
 
48
  await prisma.model.create({
49
  data: {
50
  id: model.id,
51
  image: model.image,
 
52
  likes: model.likes,
 
53
  downloads: model.downloads,
54
+ isPublic: false,
55
  }
56
+ }).catch(() => {})
57
+
58
+ index++
59
  }
60
 
61
  const total_items = await prisma.model.count()
vite.config.ts CHANGED
@@ -7,10 +7,4 @@ export default defineConfig({
7
  enhancedImages(),
8
  sveltekit(),
9
  ],
10
- // server: {
11
- // fs: {
12
- // strict: false,
13
- // allow: [".."],
14
- // },
15
- // }
16
  });
 
7
  enhancedImages(),
8
  sveltekit(),
9
  ],
 
 
 
 
 
 
10
  });