Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature | Thoughts #251

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions backend/prisma/migrations/20240713163709_thought/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- CreateTable
CREATE TABLE "Thought" (
"id" TEXT NOT NULL,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id            String     @id @default(uuid())

"content" TEXT NOT NULL,
"publishedDate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"published" BOOLEAN NOT NULL DEFAULT false,
"authorId" TEXT NOT NULL,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

author        User       @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId      String

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aadeshkulkarni what should I change here?


CONSTRAINT "Thought_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "Thought" ADD CONSTRAINT "Thought_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
10 changes: 10 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ model User {
comments Comment[]
subscribers Subscriber[] @relation("UserSubscribers")
subscribedTo Subscriber[] @relation("UserSubscribedTo")
thoughts Thought[]
}

model Post {
Expand Down Expand Up @@ -100,3 +101,12 @@ model Subscriber {
user User @relation("UserSubscribers", fields: [userId], references: [id])
subscriber User @relation("UserSubscribedTo", fields: [subscriberId], references: [id])
}

model Thought {
id String @id @default(uuid())
content String
publishedDate DateTime @default(now())
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId String
}
2 changes: 2 additions & 0 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { clapRouter } from "./routes/clap";
import { tagRouter } from "./routes/tag";
import { subscriberRouter } from "./routes/subscriber";
import { commentRouter } from "./routes/comments";
import { thoughtRouter } from "./routes/thought";

const app = new Hono<{
Bindings: {
Expand All @@ -22,5 +23,6 @@ app.route('/api/v1/clap', clapRouter)
app.route('/api/v1/tag', tagRouter)
app.route('/api/v1/subscriber', subscriberRouter)
app.route("/api/v1/comments", commentRouter)
app.route("/api/v1/thought", thoughtRouter)

export default app;
200 changes: 200 additions & 0 deletions backend/src/routes/thought.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { Hono } from "hono";
import { verify } from "hono/jwt";
import { getDBInstance } from "../db/util";

/**
*
* Introducing Thoughts. User can write a thought. Thoughts are short form. Think of 1 Thought like a Twitter post or Threads post.
*
* thoughtRouter.post
thoughtRouter.put
thoughtRouter.get: 1 and all
thoughtRouter.delete
*/
export const thoughtRouter = new Hono<{
Bindings: {
DATABASE_URL: string;
JWT_SECRET: string;
};
Variables: {
userId: string;
};
}>();


//get all thoughts of a user
thoughtRouter.get("/all/:userId", async (c) => {
try {
const prisma = getDBInstance(c)
const userId = await c.req.param("userId");
const thoughts = await prisma.thought.findMany({
where: {
authorId: userId,
},
select:{
authorId:true,
content:true,
id:true,
published:true,
publishedDate:true,
author:{
select:{
name:true
}
}

}
});

return c.json({
thoughts: thoughts
});
} catch (e) {
c.status(411);
return c.json({
message: "Error while fetching thoughts",
error: e,
});
}

})

//get a thought of a user
thoughtRouter.get("/:thoughtId", async (c) => {
try {
const prisma = getDBInstance(c)
const thoughtId = c.req.param("thoughtId");
const thought = await prisma.thought.findFirst({
where: {
id: thoughtId,
},
select:{
authorId:true,
content:true,
id:true,
published:true,
publishedDate:true,
author:{
select:{
name:true
}
}


}
});

return c.json({
thought: thought
});
} catch (e) {
c.status(411);
return c.json({
message: "Error while fetching thought",
error: e,
});
}

})

//protect all the routes below this
thoughtRouter.use("/*", async (c, next) => {
try {
const header = c.req.header("authorization") || "";
const token = header.split(" ")[1];
const user = await verify(token, c.env.JWT_SECRET);
if (user && typeof user.id === "string") {
c.set("userId", user.id);
return next();
} else {
c.status(403);
return c.json({ error: "Unauthorized " });
}
} catch (e) {
c.status(403);
return c.json({
error: "Credentials failed",
});
}
});

//create thought
thoughtRouter.post("/create", async (c) => {
try {
const prisma = getDBInstance(c)
const userId = c.get("userId");
const body = await c.req.json();
const { content } = body;
if (!content) {
c.status(400);
return c.json({
message: "Content is required",
});
}
const thought = await prisma.thought.create({
data: {
authorId: userId,
content: content,
},
});

return c.json({
id: thought.id
});
} catch (e) {
c.status(403);
return c.json({ error: "Something went wrong ", stackTrace: e });
}
})

//update thought
thoughtRouter.put("/update", async (c) => {
try {
const prisma = getDBInstance(c)
const body = await c.req.json();
const { id, content } = body;
if (!content || !id ) {
c.status(400);
return c.json({
message: "Please provide all required values",
});
}
const thought = await prisma.thought.update({
where: {
id: id,
},
data: {
content: content,
},
});

return c.json({
id: thought.id
});
} catch (e) {
c.status(403);
return c.json({ error: "Something went wrong ", stackTrace: e });
}
})

//to delete a thought
thoughtRouter.delete("/:thoughtId", async (c) => {
try {
const prisma = getDBInstance(c)
const thoughtId = c.req.param("thoughtId");
await prisma.thought.delete({
where: {
id: thoughtId,
},
});

return c.json({
message: "Thought deleted successfully",
});
} catch (e) {
c.status(411);
return c.json({
message: "Error while deleting post",
});
}
})
3 changes: 3 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { lazy, Suspense } from 'react';
import Spinner from './components/Spinner';
import { ThemeProvider } from '@/components/theme-provider';
import Thought from './pages/Thought';
// const Home = lazy(() => import('./pages/Home'));
const Signup = lazy(() => import('./pages/Signup'));
const Signin = lazy(() => import('./pages/Signin'));
Expand Down Expand Up @@ -30,6 +31,8 @@ function App() {
<Route path="/signin" element={<Signin />} />
<Route path="/blogs" element={<Blogs />} />
<Route path="/blog/:id" element={<Blog />} />
<Route path="/thoughts" element={<Thought />} />

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra line

<Route path="/publish" element={<Publish />} />
<Route path="/edit/:id" element={<Edit />} />
<Route path="/bookmarks" element={<Bookmarks />} />
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/components/Appbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ const Appbar = ({ skipAuthCheck = false, pageActions, hideWriteAction = false }:
</button>
</Link>
)}
<Link to="/thoughts">
<button
type="button"
className="focus:outline-none hover:bg-sub rounded-3xl focus:ring-4 focus:ring-gray-100 font-medium flex items-center gap-2 text-sm px-3 md:px-5 py-2.5"
>
<WriteIcon /> thought
</button>
</Link>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix indentation

<div className="ml-4">
<ProfileBox />
</div>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const BACKEND_URL = 'https://backend.aadesh-kulkarni08.workers.dev';
// export const BACKEND_URL = 'https://backend.aadesh-kulkarni08.workers.dev';
export const BACKEND_URL = 'http://localhost:39013';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BACKEND_URL should be pointing to workers.dev, not localhost. Please rollback changes to this file.

export const GITHUB_CONTRIBUTOR_URL = 'https://api.github.com/repos/aadeshkulkarni/medium-app/contributors';
// export const BACKEND_URL = "http://localhost:8787";
export const FF_ENABLE_AI = true; // Feature flag (temp solution)
Expand Down
Loading