nomadicsynth commited on
Commit
d4ce8a2
·
verified ·
2 Parent(s): d526cac 735c29b

Merge branch #enzostvs/deepsite' into 'nomadicsynth/deepsite'

Browse files
.env.example CHANGED
@@ -2,4 +2,10 @@ OAUTH_CLIENT_ID=
2
  OAUTH_CLIENT_SECRET=
3
  APP_PORT=5173
4
  REDIRECT_URI=http://localhost:5173/auth/login
5
- DEFAULT_HF_TOKEN=
 
 
 
 
 
 
 
2
  OAUTH_CLIENT_SECRET=
3
  APP_PORT=5173
4
  REDIRECT_URI=http://localhost:5173/auth/login
5
+ DEFAULT_HF_TOKEN=
6
+
7
+ # Optional
8
+ # By setting this variable, you will bypass the login page + the free requests
9
+ # and will use the token directly.
10
+ # This is useful for testing purposes or local use.
11
+ HF_TOKEN=
README.md CHANGED
@@ -18,4 +18,8 @@ models:
18
  - deepseek-ai/DeepSeek-V3-0324
19
  ---
20
 
21
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
18
  - deepseek-ai/DeepSeek-V3-0324
19
  ---
20
 
21
+ # DeepSite 🐳
22
+ DeepSite is a coding platform powered by DeepSeek AI, designed to make coding smarter and more efficient. Tailored for developers, data scientists, and AI engineers, it integrates generative AI into your coding projects to enhance creativity and productivity.
23
+
24
+ ## How to use it locally
25
+ Follow [this discussion](https://huggingface.co/spaces/enzostvs/deepsite/discussions/74)
middlewares/checkUser.js CHANGED
@@ -1,4 +1,7 @@
1
  export default async function checkUser(req, res, next) {
 
 
 
2
  const { hf_token } = req.cookies;
3
  if (!hf_token) {
4
  return res.status(401).send({
 
1
  export default async function checkUser(req, res, next) {
2
+ if (process.env.HF_TOKEN && process.env.HF_TOKEN !== "") {
3
+ return next();
4
+ }
5
  const { hf_token } = req.cookies;
6
  if (!hf_token) {
7
  return res.status(401).send({
server.js CHANGED
@@ -42,11 +42,6 @@ const getPTag = (repoId) => {
42
  };
43
 
44
  app.get("/api/login", (_req, res) => {
45
- // res.redirect(
46
- // 302,
47
- // `https://huggingface.co/oauth/authorize?client_id=${process.env.OAUTH_CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=openid%20profile%20write-repos%20manage-repos%20inference-api&prompt=consent&state=1234567890`
48
- // );
49
- // redirect in new tab
50
  const redirectUrl = `https://huggingface.co/oauth/authorize?client_id=${process.env.OAUTH_CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=openid%20profile%20write-repos%20manage-repos%20inference-api&prompt=consent&state=1234567890`;
51
  res.status(200).send({
52
  ok: true,
@@ -101,7 +96,15 @@ app.get("/auth/logout", (req, res) => {
101
  });
102
 
103
  app.get("/api/@me", checkUser, async (req, res) => {
104
- const { hf_token } = req.cookies;
 
 
 
 
 
 
 
 
105
  try {
106
  const request_user = await fetch("https://huggingface.co/oauth/userinfo", {
107
  headers: {
@@ -125,7 +128,7 @@ app.get("/api/@me", checkUser, async (req, res) => {
125
  });
126
 
127
  app.post("/api/deploy", checkUser, async (req, res) => {
128
- const { html, title, path } = req.body;
129
  if (!html || (!path && !title)) {
130
  return res.status(400).send({
131
  ok: false,
@@ -133,7 +136,11 @@ app.post("/api/deploy", checkUser, async (req, res) => {
133
  });
134
  }
135
 
136
- const { hf_token } = req.cookies;
 
 
 
 
137
  try {
138
  const repo = {
139
  type: "space",
@@ -180,7 +187,12 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
180
  const file = new Blob([newHtml], { type: "text/html" });
181
  file.name = "index.html"; // Add name property to the Blob
182
 
183
- const files = [file];
 
 
 
 
 
184
  if (readme) {
185
  const readmeFile = new Blob([readme], { type: "text/markdown" });
186
  readmeFile.name = "README.md"; // Add name property to the Blob
@@ -209,8 +221,13 @@ app.post("/api/ask-ai", async (req, res) => {
209
  });
210
  }
211
 
212
- const { hf_token } = req.cookies;
213
  let token = hf_token;
 
 
 
 
 
214
  const ip =
215
  req.headers["x-forwarded-for"]?.split(",")[0].trim() ||
216
  req.headers["x-real-ip"] ||
@@ -218,7 +235,7 @@ app.post("/api/ask-ai", async (req, res) => {
218
  req.ip ||
219
  "0.0.0.0";
220
 
221
- if (!hf_token) {
222
  ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1);
223
  if (ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) {
224
  return res.status(429).send({
@@ -244,12 +261,6 @@ app.post("/api/ask-ai", async (req, res) => {
244
  if (html) TOKENS_USED += html.length;
245
 
246
  const DEFAULT_PROVIDER = PROVIDERS.novita;
247
- // const selectedProvider =
248
- // provider === "auto"
249
- // ? TOKENS_USED < PROVIDERS.sambanova.max_tokens
250
- // ? PROVIDERS.sambanova
251
- // : DEFAULT_PROVIDER
252
- // : PROVIDERS[provider] ?? DEFAULT_PROVIDER;
253
  const selectedProvider =
254
  provider === "auto"
255
  ? DEFAULT_PROVIDER
@@ -355,7 +366,11 @@ app.get("/api/remix/:username/:repo", async (req, res) => {
355
  const { username, repo } = req.params;
356
  const { hf_token } = req.cookies;
357
 
358
- const token = hf_token || process.env.DEFAULT_HF_TOKEN;
 
 
 
 
359
 
360
  const repoId = `${username}/${repo}`;
361
 
 
42
  };
43
 
44
  app.get("/api/login", (_req, res) => {
 
 
 
 
 
45
  const redirectUrl = `https://huggingface.co/oauth/authorize?client_id=${process.env.OAUTH_CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=openid%20profile%20write-repos%20manage-repos%20inference-api&prompt=consent&state=1234567890`;
46
  res.status(200).send({
47
  ok: true,
 
96
  });
97
 
98
  app.get("/api/@me", checkUser, async (req, res) => {
99
+ let { hf_token } = req.cookies;
100
+
101
+ if (process.env.HF_TOKEN && process.env.HF_TOKEN !== "") {
102
+ return res.send({
103
+ preferred_username: "local-use",
104
+ isLocalUse: true,
105
+ });
106
+ }
107
+
108
  try {
109
  const request_user = await fetch("https://huggingface.co/oauth/userinfo", {
110
  headers: {
 
128
  });
129
 
130
  app.post("/api/deploy", checkUser, async (req, res) => {
131
+ const { html, title, path, prompts } = req.body;
132
  if (!html || (!path && !title)) {
133
  return res.status(400).send({
134
  ok: false,
 
136
  });
137
  }
138
 
139
+ let { hf_token } = req.cookies;
140
+ if (process.env.HF_TOKEN && process.env.HF_TOKEN !== "") {
141
+ hf_token = process.env.HF_TOKEN;
142
+ }
143
+
144
  try {
145
  const repo = {
146
  type: "space",
 
187
  const file = new Blob([newHtml], { type: "text/html" });
188
  file.name = "index.html"; // Add name property to the Blob
189
 
190
+ // create prompt.txt file with all the prompts used, split by new line
191
+ const newPrompts = ``.concat(prompts.map((prompt) => prompt).join("\n"));
192
+ const promptFile = new Blob([newPrompts], { type: "text/plain" });
193
+ promptFile.name = "prompts.txt"; // Add name property to the Blob
194
+
195
+ const files = [file, promptFile];
196
  if (readme) {
197
  const readmeFile = new Blob([readme], { type: "text/markdown" });
198
  readmeFile.name = "README.md"; // Add name property to the Blob
 
221
  });
222
  }
223
 
224
+ let { hf_token } = req.cookies;
225
  let token = hf_token;
226
+
227
+ if (process.env.HF_TOKEN && process.env.HF_TOKEN !== "") {
228
+ token = process.env.HF_TOKEN;
229
+ }
230
+
231
  const ip =
232
  req.headers["x-forwarded-for"]?.split(",")[0].trim() ||
233
  req.headers["x-real-ip"] ||
 
235
  req.ip ||
236
  "0.0.0.0";
237
 
238
+ if (!token) {
239
  ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1);
240
  if (ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) {
241
  return res.status(429).send({
 
261
  if (html) TOKENS_USED += html.length;
262
 
263
  const DEFAULT_PROVIDER = PROVIDERS.novita;
 
 
 
 
 
 
264
  const selectedProvider =
265
  provider === "auto"
266
  ? DEFAULT_PROVIDER
 
366
  const { username, repo } = req.params;
367
  const { hf_token } = req.cookies;
368
 
369
+ let token = hf_token || process.env.DEFAULT_HF_TOKEN;
370
+
371
+ if (process.env.HF_TOKEN && process.env.HF_TOKEN !== "") {
372
+ token = process.env.HF_TOKEN;
373
+ }
374
 
375
  const repoId = `${username}/${repo}`;
376
 
src/components/App.tsx CHANGED
@@ -36,6 +36,7 @@ function App() {
36
  const [currentView, setCurrentView] = useState<"editor" | "preview">(
37
  "editor"
38
  );
 
39
 
40
  const fetchMe = async () => {
41
  const res = await fetch("/api/@me");
@@ -182,7 +183,13 @@ function App() {
182
  }
183
  }}
184
  >
185
- <DeployButton html={html} error={error} auth={auth} setHtml={setHtml} />
 
 
 
 
 
 
186
  </Header>
187
  <main className="max-lg:flex-col flex w-full">
188
  <div
@@ -233,6 +240,9 @@ function App() {
233
  isAiWorking={isAiWorking}
234
  setisAiWorking={setisAiWorking}
235
  setView={setCurrentView}
 
 
 
236
  onScrollToBottom={() => {
237
  editorRef.current?.revealLine(
238
  editorRef.current?.getModel()?.getLineCount() ?? 0
 
36
  const [currentView, setCurrentView] = useState<"editor" | "preview">(
37
  "editor"
38
  );
39
+ const [prompts, setPrompts] = useState<string[]>([]);
40
 
41
  const fetchMe = async () => {
42
  const res = await fetch("/api/@me");
 
183
  }
184
  }}
185
  >
186
+ <DeployButton
187
+ html={html}
188
+ error={error}
189
+ auth={auth}
190
+ setHtml={setHtml}
191
+ prompts={prompts}
192
+ />
193
  </Header>
194
  <main className="max-lg:flex-col flex w-full">
195
  <div
 
240
  isAiWorking={isAiWorking}
241
  setisAiWorking={setisAiWorking}
242
  setView={setCurrentView}
243
+ onNewPrompt={(prompt) => {
244
+ setPrompts((prev) => [...prev, prompt]);
245
+ }}
246
  onScrollToBottom={() => {
247
  editorRef.current?.revealLine(
248
  editorRef.current?.getModel()?.getLineCount() ?? 0
src/components/ask-ai/ask-ai.tsx CHANGED
@@ -21,11 +21,13 @@ function AskAI({
21
  isAiWorking,
22
  setisAiWorking,
23
  setView,
 
24
  }: {
25
  html: string;
26
  setHtml: (html: string) => void;
27
  onScrollToBottom: () => void;
28
  isAiWorking: boolean;
 
29
  setView: React.Dispatch<React.SetStateAction<"editor" | "preview">>;
30
  setisAiWorking: React.Dispatch<React.SetStateAction<boolean>>;
31
  }) {
@@ -49,6 +51,7 @@ function AskAI({
49
  let contentResponse = "";
50
  let lastRenderTime = 0;
51
  try {
 
52
  const request = await fetch("/api/ask-ai", {
53
  method: "POST",
54
  body: JSON.stringify({
 
21
  isAiWorking,
22
  setisAiWorking,
23
  setView,
24
+ onNewPrompt,
25
  }: {
26
  html: string;
27
  setHtml: (html: string) => void;
28
  onScrollToBottom: () => void;
29
  isAiWorking: boolean;
30
+ onNewPrompt: (prompt: string) => void;
31
  setView: React.Dispatch<React.SetStateAction<"editor" | "preview">>;
32
  setisAiWorking: React.Dispatch<React.SetStateAction<boolean>>;
33
  }) {
 
51
  let contentResponse = "";
52
  let lastRenderTime = 0;
53
  try {
54
+ onNewPrompt(prompt);
55
  const request = await fetch("/api/ask-ai", {
56
  method: "POST",
57
  body: JSON.stringify({
src/components/deploy-button/deploy-button.tsx CHANGED
@@ -29,11 +29,13 @@ function DeployButton({
29
  error = false,
30
  auth,
31
  setHtml,
 
32
  }: {
33
  html: string;
34
  error: boolean;
35
  auth?: Auth;
36
  setHtml: (html: string) => void;
 
37
  }) {
38
  const [open, setOpen] = useState(false);
39
  const [loading, setLoading] = useState(false);
@@ -53,6 +55,7 @@ function DeployButton({
53
  title: config.title,
54
  path,
55
  html,
 
56
  }),
57
  headers: {
58
  "Content-Type": "application/json",
@@ -84,31 +87,38 @@ function DeployButton({
84
  <div className="flex items-center justify-end gap-5">
85
  <LoadButton auth={auth} setHtml={setHtml} setPath={setPath} />
86
  <div className="relative flex items-center justify-end">
87
- {auth && (
88
- <>
89
- <button
90
- className="mr-2 cursor-pointer"
91
- onClick={() => {
92
- if (confirm("Are you sure you want to log out?")) {
93
- // go to /auth/logout page
94
- window.location.href = "/auth/logout";
95
- }
96
- }}
97
- >
98
- <FaPowerOff className="text-lg text-red-500" />
99
- </button>
100
- <p className="mr-3 text-xs lg:text-sm text-gray-300">
101
- <span className="max-lg:hidden">Connected as </span>
102
- <a
103
- href={`https://huggingface.co/${auth.preferred_username}`}
104
- target="_blank"
105
- className="underline hover:text-white"
106
  >
107
- {auth.preferred_username}
108
- </a>
109
- </p>
110
- </>
111
- )}
 
 
 
 
 
 
 
 
 
112
  <button
113
  className={classNames(
114
  "relative cursor-pointer flex-none flex items-center justify-center rounded-md text-xs lg:text-sm font-semibold leading-5 lg:leading-6 py-1.5 px-5 hover:bg-pink-400 text-white shadow-sm dark:shadow-highlight/20",
 
29
  error = false,
30
  auth,
31
  setHtml,
32
+ prompts,
33
  }: {
34
  html: string;
35
  error: boolean;
36
  auth?: Auth;
37
  setHtml: (html: string) => void;
38
+ prompts: string[];
39
  }) {
40
  const [open, setOpen] = useState(false);
41
  const [loading, setLoading] = useState(false);
 
55
  title: config.title,
56
  path,
57
  html,
58
+ prompts,
59
  }),
60
  headers: {
61
  "Content-Type": "application/json",
 
87
  <div className="flex items-center justify-end gap-5">
88
  <LoadButton auth={auth} setHtml={setHtml} setPath={setPath} />
89
  <div className="relative flex items-center justify-end">
90
+ {auth &&
91
+ (auth.isLocalUse ? (
92
+ <>
93
+ <div className="bg-amber-500/10 border border-amber-10 text-amber-500 font-semibold leading-5 lg:leading-6 py-1 px-5 text-xs lg:text-sm rounded-md mr-4 select-none">
94
+ Local Usage
95
+ </div>
96
+ </>
97
+ ) : (
98
+ <>
99
+ <button
100
+ className="mr-2 cursor-pointer"
101
+ onClick={() => {
102
+ if (confirm("Are you sure you want to log out?")) {
103
+ // go to /auth/logout page
104
+ window.location.href = "/auth/logout";
105
+ }
106
+ }}
 
 
107
  >
108
+ <FaPowerOff className="text-lg text-red-500" />
109
+ </button>
110
+ <p className="mr-3 text-xs lg:text-sm text-gray-300">
111
+ <span className="max-lg:hidden">Connected as </span>
112
+ <a
113
+ href={`https://huggingface.co/${auth.preferred_username}`}
114
+ target="_blank"
115
+ className="underline hover:text-white"
116
+ >
117
+ {auth.preferred_username}
118
+ </a>
119
+ </p>
120
+ </>
121
+ ))}
122
  <button
123
  className={classNames(
124
  "relative cursor-pointer flex-none flex items-center justify-center rounded-md text-xs lg:text-sm font-semibold leading-5 lg:leading-6 py-1.5 px-5 hover:bg-pink-400 text-white shadow-sm dark:shadow-highlight/20",
src/components/settings/settings.tsx CHANGED
@@ -54,6 +54,16 @@ function Settings({
54
  <main className="px-4 pt-3 pb-4 space-y-4">
55
  {/* toggle using tailwind css */}
56
  <div>
 
 
 
 
 
 
 
 
 
 
57
  <div className="flex items-center justify-between">
58
  <p className="text-gray-800 text-sm font-medium flex items-center justify-between">
59
  Use auto-provider
 
54
  <main className="px-4 pt-3 pb-4 space-y-4">
55
  {/* toggle using tailwind css */}
56
  <div>
57
+ <a
58
+ href="https://huggingface.co/spaces/enzostvs/deepsite/discussions/74"
59
+ target="_blank"
60
+ className="w-full flex items-center justify-between text-gray-600 bg-gray-50 border border-gray-100 px-2 py-2 rounded-lg mb-3 text-sm font-medium hover:brightness-95"
61
+ >
62
+ How to use it locally?
63
+ <button className="bg-black text-white rounded-md px-3 py-1.5 text-xs font-semibold cursor-pointer">
64
+ See the guide
65
+ </button>
66
+ </a>
67
  <div className="flex items-center justify-between">
68
  <p className="text-gray-800 text-sm font-medium flex items-center justify-between">
69
  Use auto-provider
utils/types.ts CHANGED
@@ -2,4 +2,5 @@ export interface Auth {
2
  preferred_username: string;
3
  picture: string;
4
  name: string;
 
5
  }
 
2
  preferred_username: string;
3
  picture: string;
4
  name: string;
5
+ isLocalUse?: boolean;
6
  }