Visual improvements

This commit is contained in:
Max 2025-02-03 23:42:08 +03:00
parent 6054bc0403
commit ad1aff3692
34 changed files with 278 additions and 73 deletions

View File

@ -3,3 +3,24 @@ export type TUser = {
isAdmin: boolean; isAdmin: boolean;
id?: string | number; id?: string | number;
} }
export type TInfoUser = {
user_id: number;
username: string;
email: string;
createdAt: string;
is_admin: boolean;
display_name: string;
};
export type TProfile = {
user_id: number;
bio: string;
avatar_url: string;
website_url: string;
};
export type TGetUserInfoResponse = {
user_info: TInfoUser;
profile_info: TProfile;
};

View File

@ -38,7 +38,7 @@ export default function ArticleViewer(props: TArticleViewer) {
<> <>
<Container size={"3"}> <Container size={"3"}>
<div className="ql-snow ql-editor"> <div className="ql-snow ql-editor">
<Container size={"2"} className="mt-4"> <Container size={"2"} className="mt-0">
<Flex direction={"column"}> <Flex direction={"column"}>
<Text className="mb-2" as="div" size={"9"}> <Text className="mb-2" as="div" size={"9"}>
{data.title} {data.title}

View File

@ -1,15 +1,18 @@
import { Flex } from "@radix-ui/themes";
import CustomNavigationMenu from "./NavigationMenu/NavigationMenu"; import CustomNavigationMenu from "./NavigationMenu/NavigationMenu";
import RightButtonBar from "./RightButtonBar/RightButtonBar"; import RightButtonBar from "./RightButtonBar/RightButtonBar";
import SearchField from "./SearchField/SearchField"; import SearchField from "./SearchField/SearchField";
export default function NavBar() { export default function NavBar() {
return ( return (
<nav className="flex justify-center pt-2 pb-2 ml-4 mr-4 flex-[1] max-h-fit"> <Flex className="justify-center w-full">
<nav className="flex justify-center pt-2 pb-2 ml-4 mr-4 mb-4 mt-2 flex-[1] max-h-fit max-w-[70rem]">
<CustomNavigationMenu /> <CustomNavigationMenu />
<SearchField /> <SearchField />
<RightButtonBar /> <RightButtonBar />
</nav> </nav>
</Flex>
); );
} }

View File

@ -8,7 +8,7 @@ export default function SearchField() {
return ( return (
<div className="flex justify-center flex-1"> <div className="flex justify-center flex-1">
<TextField.Root <TextField.Root
className="w-2/3 rounded-lg" className="hidden w-2/3 rounded-lg"
placeholder={t("search")} placeholder={t("search")}
> >
<TextField.Slot> <TextField.Slot>

View File

@ -38,10 +38,7 @@ export default function ProfileNavbar() {
<UserCard /> <UserCard />
<Box className="mx-2"> <Box className="mx-2">
<Separator <Separator orientation={"horizontal"} size={"4"} />
orientation={"horizontal"}
size={"4"}
/>
</Box> </Box>
<Flex gap={"2"} direction={"column"}> <Flex gap={"2"} direction={"column"}>

View File

@ -1,8 +1,18 @@
import { Avatar, Badge, Card, Flex, Separator, Text } from "@radix-ui/themes"; import {
Avatar,
Badge,
Card,
Flex,
Separator,
Skeleton,
Text,
} from "@radix-ui/themes";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { TGetUserInfoResponse } from "../../@types/UserType";
import { userAtom } from "../../AtomStore/AtomStore"; import { userAtom } from "../../AtomStore/AtomStore";
import { axiosLocalhost } from "../../api/axios/axios"; import { axiosLocalhost } from "../../api/axios/axios";
import { JSONWithInt64 } from "../../utils/idnex";
type TUserCard = { type TUserCard = {
username?: string; username?: string;
@ -12,13 +22,22 @@ type TUserCard = {
export default function UserCard(props: TUserCard) { export default function UserCard(props: TUserCard) {
const user = useAtomValue(userAtom); const user = useAtomValue(userAtom);
const { data } = useQuery({ const { data, isLoading } = useQuery({
queryKey: [`userCard${props.userId}`], queryKey: [`userCard${props.userId || user?.id}`],
queryFn: async () => { queryFn: async () => {
const response = await axiosLocalhost.get(`/users/${props.userId}`); const response = await axiosLocalhost.get(
return response.data; `/users/info/${props.userId || user?.id?.toString()}`,
{
transformResponse: [(data) => data],
}
);
const parsedResponse = JSONWithInt64(response.data);
console.log("parsedResponse", parsedResponse);
return parsedResponse as TGetUserInfoResponse;
}, },
enabled: !!props.userId,
}); });
const getInitials = (username: string): string => { const getInitials = (username: string): string => {
@ -42,11 +61,22 @@ export default function UserCard(props: TUserCard) {
<Avatar <Avatar
fallback={<div>{getInitials(getUsername())}</div>} fallback={<div>{getInitials(getUsername())}</div>}
radius="full" radius="full"
loading="lazy"
/> />
<Flex className="flex-col "> <Flex className="flex-col overflow-hidden">
<Text>Test User</Text> {isLoading ? (
<Skeleton>
<Text truncate>{`Temporal`}</Text>
</Skeleton>
) : (
<Text truncate>
{data?.user_info.display_name ||
`Non specified`}
</Text>
)}
<Text size={"1"} color={"gray"}> <Text size={"1"} color={"gray"}>
@testuser123 {`@${getUsername()}`}
</Text> </Text>
</Flex> </Flex>
</Flex> </Flex>

View File

@ -9,6 +9,7 @@ import { Link, useNavigate } from "react-router-dom";
import { axiosLocalhost } from "../../../api/axios/axios"; import { axiosLocalhost } from "../../../api/axios/axios";
import { userAtom } from "../../../AtomStore/AtomStore"; import { userAtom } from "../../../AtomStore/AtomStore";
import UseCapsLock from "../../../hooks/useCapsLock"; import UseCapsLock from "../../../hooks/useCapsLock";
import { JSONWithInt64 } from "../../../utils/idnex";
import ShowPasswordButton from "../ShowPasswordButton/ShowPasswordButton"; import ShowPasswordButton from "../ShowPasswordButton/ShowPasswordButton";
type TLoginData = { type TLoginData = {
@ -28,12 +29,18 @@ export default function LoginPage() {
mutationFn: async (data: TLoginData) => { mutationFn: async (data: TLoginData) => {
let response = await axiosLocalhost.post( let response = await axiosLocalhost.post(
"/login", "/login",
JSON.stringify(data) JSON.stringify(data),
{
transformResponse: [data => data]
}
); );
const parsedData = JSONWithInt64(response.data);
setUserAtom({ setUserAtom({
username: response.data.username, username: parsedData.username,
isAdmin: false, isAdmin: false,
id: response.data.id, id: parsedData.id,
}); });
}, },

View File

@ -9,6 +9,7 @@ import { Link, useNavigate } from "react-router-dom";
import { axiosLocalhost } from "../../../api/axios/axios"; import { axiosLocalhost } from "../../../api/axios/axios";
import { userAtom } from "../../../AtomStore/AtomStore"; import { userAtom } from "../../../AtomStore/AtomStore";
import UseCapsLock from "../../../hooks/useCapsLock"; import UseCapsLock from "../../../hooks/useCapsLock";
import { JSONWithInt64 } from "../../../utils/idnex";
import ShowPasswordButton from "../ShowPasswordButton/ShowPasswordButton"; import ShowPasswordButton from "../ShowPasswordButton/ShowPasswordButton";
type TRegisterData = { type TRegisterData = {
@ -31,11 +32,16 @@ export default function RegisterPage() {
const registerMutation = useMutation({ const registerMutation = useMutation({
mutationFn: async (data: TRegisterData) => { mutationFn: async (data: TRegisterData) => {
let response = await axiosLocalhost.post("/users", JSON.stringify(data)); let response = await axiosLocalhost.post("/users", JSON.stringify(data), {
transformResponse: [data => data]
});
const parsedResponse = JSONWithInt64(response.data)
setUserAtom({ setUserAtom({
username: response.data.username, username: parsedResponse.username,
isAdmin: false, isAdmin: false,
id: response.data.id, id: parsedResponse.id,
}) })
}, },

View File

@ -15,7 +15,7 @@ export default function UserBlogsPage() {
transformResponse: [(data) => data], transformResponse: [(data) => data],
}); });
let temp = JSONWithInt64(response.data); const temp = JSONWithInt64(response.data);
return temp as any[]; return temp as any[];
}, },

View File

@ -1,5 +1,13 @@
import { Flex, ScrollArea, Text } from "@radix-ui/themes"; import {
import { loremText } from "../../constants/loremText"; Badge,
Box,
DataList,
Flex,
ScrollArea,
Separator,
Text,
TextArea,
} from "@radix-ui/themes";
export default function UserProfilePage() { export default function UserProfilePage() {
return ( return (
@ -7,11 +15,68 @@ export default function UserProfilePage() {
<ScrollArea <ScrollArea
type="auto" type="auto"
scrollbars="vertical" scrollbars="vertical"
className="flex-grow-[1] pr-5" className="flex-grow-[1] pt-4"
> >
<Text> <Text size={"8"}>Base info</Text>
<p className="text-justify">{loremText}</p>
<Separator className="w-full my-4" />
<Box className="mb-8">
<DataList.Root size={"3"}>
<DataList.Item className="items-center">
<DataList.Label className="min-w-40">
Username
</DataList.Label>
<DataList.Value>
@Definitely_fake_user
</DataList.Value>
</DataList.Item>
<DataList.Item>
<DataList.Label className="min-w-40">
Email
</DataList.Label>
<DataList.Value>fake@email.com</DataList.Value>
</DataList.Item>
<DataList.Item>
<DataList.Label className="min-w-40">
Display name
</DataList.Label>
<DataList.Value>Isaev</DataList.Value>
</DataList.Item>
<DataList.Item>
<DataList.Label className="min-w-40">
Badges
</DataList.Label>
<DataList.Value>
<Flex
gap={"2"}
wrap={"wrap"}
className="content-evenly"
>
<Badge size={"3"}>User</Badge>
<Badge size={"3"} color="red">
Admin
</Badge>
<Badge size={"3"} color="green">
Writer
</Badge>
</Flex>
</DataList.Value>
</DataList.Item>
</DataList.Root>
</Box>
<Text size={"8"}>
Bio
</Text> </Text>
<Separator className="w-full my-4" />
<TextArea resize={"vertical"} placeholder="Add your bio here...">
</TextArea>
</ScrollArea> </ScrollArea>
</Flex> </Flex>
); );

View File

@ -5,6 +5,7 @@ import { Outlet } from "react-router-dom";
import { axiosLocalhost } from "../../api/axios/axios"; import { axiosLocalhost } from "../../api/axios/axios";
import { userAtom } from "../../AtomStore/AtomStore"; import { userAtom } from "../../AtomStore/AtomStore";
import NavBar from "../../Components/NavBar/NavBar"; import NavBar from "../../Components/NavBar/NavBar";
import { JSONWithInt64 } from "../../utils/idnex";
const REFETCH_INTERVAL_IN_MINUTES = 5; const REFETCH_INTERVAL_IN_MINUTES = 5;
const RETRY_INTERVAL_IN_SECONDS = 1; const RETRY_INTERVAL_IN_SECONDS = 1;
@ -19,12 +20,16 @@ export default function MainPage() {
queryKey: ["authKey"], queryKey: ["authKey"],
queryFn: async () => { queryFn: async () => {
try { try {
const response = await axiosLocalhost.get("/auth/check"); const response = await axiosLocalhost.get("/auth/check", {
transformResponse: [data => data]
});
const parsedResponse = JSONWithInt64(response.data)
setUserData({ setUserData({
isAdmin: response.data["is_admin"], isAdmin: parsedResponse["is_admin"],
username: response.data["username"], username: parsedResponse["username"],
id: response.data["id"], id: parsedResponse["id"],
}); });
return true; return true;
} catch (error) { } catch (error) {

View File

@ -8,12 +8,13 @@ export default function ProfilePage() {
className={` className={`
relative flex-col flex-1 gap-0 mx-4 relative flex-col flex-1 gap-0 mx-4
sm:flex-row sm:gap-4 sm:flex-row sm:gap-4
md:w-full md:min-w-[45rem] md:max-w-[50rem] md:mx-auto md:w-full md:min-w-[45rem] md:max-w-[70rem] md:mx-auto
lg:min-w-[50rem] lg:mx-auto
overflow-hidden overflow-hidden
`} `}
> >
<Box className="">
<ProfileNavbar /> <ProfileNavbar />
</Box>
<Box className="my-2 collapse sm:visible"> <Box className="my-2 collapse sm:visible">
<Separator orientation="vertical" size={"4"} /> <Separator orientation="vertical" size={"4"} />

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: blogs_queries.sql // source: blogs_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: bookmarks_queries.sql // source: bookmarks_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: categories_queries.sql // source: categories_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: comments_queries.sql // source: comments_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: favorites_queries.sql // source: favorites_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: likes_queries.sql // source: likes_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
package db_repo package db_repo
@ -89,4 +89,5 @@ type User struct {
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required"`
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"is_admin"`
DisplayName pgtype.Text `json:"display_name"`
} }

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: multi_queries.sql // source: multi_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: post_tags_queries.sql // source: post_tags_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: post_votes_queries.sql // source: post_votes_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: posts_queries.sql // source: posts_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: profiles_queries.sql // source: profiles_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: tags_queries.sql // source: tags_queries.sql
package db_repo package db_repo

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.28.0
// source: users_queries.sql // source: users_queries.sql
package db_repo package db_repo
@ -13,7 +13,7 @@ const createUser = `-- name: CreateUser :one
INSERT INTO public.users INSERT INTO public.users
(user_id, username, email, "password", created_at, is_admin) (user_id, username, email, "password", created_at, is_admin)
VALUES($1, $2, $3, $4, CURRENT_TIMESTAMP, false) VALUES($1, $2, $3, $4, CURRENT_TIMESTAMP, false)
RETURNING user_id, username, email, password, created_at, is_admin RETURNING user_id, username, email, password, created_at, is_admin, display_name
` `
type CreateUserParams struct { type CreateUserParams struct {
@ -38,6 +38,7 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e
&i.Password, &i.Password,
&i.CreatedAt, &i.CreatedAt,
&i.IsAdmin, &i.IsAdmin,
&i.DisplayName,
) )
return i, err return i, err
} }
@ -63,7 +64,7 @@ func (q *Queries) DeleteUserByUsername(ctx context.Context, username string) err
} }
const getAllUsers = `-- name: GetAllUsers :many const getAllUsers = `-- name: GetAllUsers :many
SELECT user_id, username, email, password, created_at, is_admin FROM users SELECT user_id, username, email, password, created_at, is_admin, display_name FROM users
` `
func (q *Queries) GetAllUsers(ctx context.Context) ([]User, error) { func (q *Queries) GetAllUsers(ctx context.Context) ([]User, error) {
@ -82,6 +83,7 @@ func (q *Queries) GetAllUsers(ctx context.Context) ([]User, error) {
&i.Password, &i.Password,
&i.CreatedAt, &i.CreatedAt,
&i.IsAdmin, &i.IsAdmin,
&i.DisplayName,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -94,7 +96,7 @@ func (q *Queries) GetAllUsers(ctx context.Context) ([]User, error) {
} }
const getUserByEmailOrNickname = `-- name: GetUserByEmailOrNickname :one const getUserByEmailOrNickname = `-- name: GetUserByEmailOrNickname :one
SELECT user_id, username, email, password, created_at, is_admin FROM users WHERE username = $1 OR email = $2 LIMIT 1 SELECT user_id, username, email, password, created_at, is_admin, display_name FROM users WHERE username = $1 OR email = $2 LIMIT 1
` `
type GetUserByEmailOrNicknameParams struct { type GetUserByEmailOrNicknameParams struct {
@ -112,12 +114,13 @@ func (q *Queries) GetUserByEmailOrNickname(ctx context.Context, arg GetUserByEma
&i.Password, &i.Password,
&i.CreatedAt, &i.CreatedAt,
&i.IsAdmin, &i.IsAdmin,
&i.DisplayName,
) )
return i, err return i, err
} }
const getUserById = `-- name: GetUserById :one const getUserById = `-- name: GetUserById :one
SELECT user_id, username, email, password, created_at, is_admin FROM users WHERE user_id = $1 SELECT user_id, username, email, password, created_at, is_admin, display_name FROM users WHERE user_id = $1
` `
func (q *Queries) GetUserById(ctx context.Context, userID int64) (User, error) { func (q *Queries) GetUserById(ctx context.Context, userID int64) (User, error) {
@ -130,12 +133,13 @@ func (q *Queries) GetUserById(ctx context.Context, userID int64) (User, error) {
&i.Password, &i.Password,
&i.CreatedAt, &i.CreatedAt,
&i.IsAdmin, &i.IsAdmin,
&i.DisplayName,
) )
return i, err return i, err
} }
const getUserByUsername = `-- name: GetUserByUsername :one const getUserByUsername = `-- name: GetUserByUsername :one
SELECT user_id, username, email, password, created_at, is_admin FROM users WHERE username = $1 SELECT user_id, username, email, password, created_at, is_admin, display_name FROM users WHERE username = $1
` `
func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User, error) { func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User, error) {
@ -148,6 +152,7 @@ func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User,
&i.Password, &i.Password,
&i.CreatedAt, &i.CreatedAt,
&i.IsAdmin, &i.IsAdmin,
&i.DisplayName,
) )
return i, err return i, err
} }
@ -167,7 +172,7 @@ const updateUserPasswordHash = `-- name: UpdateUserPasswordHash :one
UPDATE public.users UPDATE public.users
SET "password"=$1 SET "password"=$1
WHERE user_id=$2 WHERE user_id=$2
RETURNING user_id, username, email, password, created_at, is_admin RETURNING user_id, username, email, password, created_at, is_admin, display_name
` `
type UpdateUserPasswordHashParams struct { type UpdateUserPasswordHashParams struct {
@ -185,6 +190,7 @@ func (q *Queries) UpdateUserPasswordHash(ctx context.Context, arg UpdateUserPass
&i.Password, &i.Password,
&i.CreatedAt, &i.CreatedAt,
&i.IsAdmin, &i.IsAdmin,
&i.DisplayName,
) )
return i, err return i, err
} }

View File

@ -5,7 +5,7 @@ COMMENT ON SCHEMA "public" IS 'standard public schema';
-- Create "categories" table -- Create "categories" table
CREATE TABLE "public"."categories" ("category_id" integer NOT NULL, "category_name" character varying(50) NOT NULL, PRIMARY KEY ("category_id"), CONSTRAINT "categories_category_name_key" UNIQUE ("category_name")); CREATE TABLE "public"."categories" ("category_id" integer NOT NULL, "category_name" character varying(50) NOT NULL, PRIMARY KEY ("category_id"), CONSTRAINT "categories_category_name_key" UNIQUE ("category_name"));
-- Create "users" table -- Create "users" table
CREATE TABLE "public"."users" ("user_id" bigint NOT NULL, "username" character varying(50) NOT NULL, "email" character varying(100) NOT NULL, "password" character varying(255) NOT NULL, "created_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, "is_admin" boolean NOT NULL, PRIMARY KEY ("user_id"), CONSTRAINT "users_email_key" UNIQUE ("email"), CONSTRAINT "users_username_key" UNIQUE ("username")); CREATE TABLE "public"."users" ("user_id" bigint NOT NULL, "username" character varying(50) NOT NULL, "email" character varying(100) NOT NULL, "password" character varying(255) NOT NULL, "created_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, "is_admin" boolean NOT NULL, "display_name" character varying(32) NULL, PRIMARY KEY ("user_id"), CONSTRAINT "users_email_key" UNIQUE ("email"), CONSTRAINT "users_username_key" UNIQUE ("username"));
-- Create "blogs" table -- Create "blogs" table
CREATE TABLE "public"."blogs" ("blog_id" bigint NOT NULL, "user_id" bigint NOT NULL, "title" character varying(255) NULL, "description" text NULL, "category_id" integer NULL, "created_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("blog_id"), CONSTRAINT "blogs_category_id_fkey" FOREIGN KEY ("category_id") REFERENCES "public"."categories" ("category_id") ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT "blogs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE); CREATE TABLE "public"."blogs" ("blog_id" bigint NOT NULL, "user_id" bigint NOT NULL, "title" character varying(255) NULL, "description" text NULL, "category_id" integer NULL, "created_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("blog_id"), CONSTRAINT "blogs_category_id_fkey" FOREIGN KEY ("category_id") REFERENCES "public"."categories" ("category_id") ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT "blogs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
-- Create "posts" table -- Create "posts" table
@ -23,7 +23,7 @@ CREATE TABLE "public"."tags" ("tag_id" integer NOT NULL, "tag_name" character va
-- Create "post_tags" table -- Create "post_tags" table
CREATE TABLE "public"."post_tags" ("post_id" bigint NOT NULL, "tag_id" integer NOT NULL, PRIMARY KEY ("post_id", "tag_id"), CONSTRAINT "post_tags_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "post_tags_tag_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "public"."tags" ("tag_id") ON UPDATE NO ACTION ON DELETE CASCADE); CREATE TABLE "public"."post_tags" ("post_id" bigint NOT NULL, "tag_id" integer NOT NULL, PRIMARY KEY ("post_id", "tag_id"), CONSTRAINT "post_tags_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "post_tags_tag_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "public"."tags" ("tag_id") ON UPDATE NO ACTION ON DELETE CASCADE);
-- Create "post_votes" table -- Create "post_votes" table
CREATE TABLE "public"."post_votes" ("post_id" bigint NOT NULL, "user_id" bigint NOT NULL, "vote" boolean NOT NULL, PRIMARY KEY ("post_id", "user_id"), CONSTRAINT "post_votes_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT "post_votes_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE NO ACTION); CREATE TABLE "public"."post_votes" ("post_id" bigint NOT NULL, "user_id" bigint NOT NULL, "vote" boolean NOT NULL, PRIMARY KEY ("post_id", "user_id"), CONSTRAINT "post_votes_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "post_votes_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE NO ACTION);
-- Create "profiles" table -- Create "profiles" table
CREATE TABLE "public"."profiles" ("user_id" bigint NOT NULL, "bio" text NULL, "avatar_url" character varying(255) NULL, "website_url" character varying(100) NULL, PRIMARY KEY ("user_id"), CONSTRAINT "profiles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE); CREATE TABLE "public"."profiles" ("user_id" bigint NOT NULL, "bio" text NULL, "avatar_url" character varying(255) NULL, "website_url" character varying(100) NULL, PRIMARY KEY ("user_id"), CONSTRAINT "profiles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
-- Create index "profiles_user_id_idx" to table: "profiles" -- Create index "profiles_user_id_idx" to table: "profiles"

View File

@ -22,7 +22,7 @@ func TargetMiddleware() gin.HandlerFunc {
case "POST": case "POST":
c.Set("target", POST) c.Set("target", POST)
case "GET": case "GET":
c.Set("target", DELETE) c.Set("target", GET)
} }
c.Next() c.Next()

View File

@ -16,7 +16,7 @@ func GetClaimsFromContext(c *gin.Context) (auth.UserInfoJWT, error) {
claims, exists := c.Get(global.ContextTokenData) claims, exists := c.Get(global.ContextTokenData)
if !exists { if !exists {
return auth.UserInfoJWT{}, fmt.Errorf("error getting user id") return auth.UserInfoJWT{}, fmt.Errorf("error getting user id 1")
} }
parsedUserId, err := strconv.ParseInt( parsedUserId, err := strconv.ParseInt(

View File

@ -12,7 +12,7 @@ func GetUserIdFromContext(c *gin.Context) (int64, error) {
userId, exists := c.Get(global.ContextUserId) userId, exists := c.Get(global.ContextUserId)
if !exists { if !exists {
return -1, fmt.Errorf("error getting user id") return -1, fmt.Errorf("error getting user id 2")
} }
if parsedUserId, err := strconv.ParseInt(userId.(string), 10, 64); err != nil { if parsedUserId, err := strconv.ParseInt(userId.(string), 10, 64); err != nil {

View File

@ -14,6 +14,7 @@ const (
POST_BLOG_MIDDLEWARE = "POST_BLOG_MIDDLEWARE" POST_BLOG_MIDDLEWARE = "POST_BLOG_MIDDLEWARE"
POST_VOTE_MIDDLEWARE = "POST_VOTE_MIDDLEWARE" POST_VOTE_MIDDLEWARE = "POST_VOTE_MIDDLEWARE"
POST_VOTES_MIDDLEWARE = "POST_VOTES_MIDDLEWARE" POST_VOTES_MIDDLEWARE = "POST_VOTES_MIDDLEWARE"
USER_MIDDLEWARE = "USER_MIDDLEWARE"
) )
var MiddlewareProvider = middleware.MiddlewareProvider{ var MiddlewareProvider = middleware.MiddlewareProvider{
@ -152,6 +153,25 @@ var policiesToRegister = map[string]middleware.RulesToCheck{
MustBeCompleted: rules.ALL_RULES_MUST_BE_COMPLETED, MustBeCompleted: rules.ALL_RULES_MUST_BE_COMPLETED,
}, },
}, },
USER_MIDDLEWARE: {
middleware.GET: {
Rules: make([]rules.RuleFunction, 0),
MustBeCompleted: rules.ALL_RULES_MUST_BE_COMPLETED,
},
middleware.PUT: {
Rules: []rules.RuleFunction{
globalrules.AuthorizedRule,
},
MustBeCompleted: rules.ALL_RULES_MUST_BE_COMPLETED,
},
middleware.DELETE: {
Rules: []rules.RuleFunction{
globalrules.IsAdminRule,
},
MustBeCompleted: rules.ALL_RULES_MUST_BE_COMPLETED,
},
},
} }
func InitMiddlewareProvider() { func InitMiddlewareProvider() {

View File

@ -194,6 +194,14 @@ func SetupRotes(g *gin.Engine) error {
voteroutes.GetVotes, voteroutes.GetVotes,
) )
userGroup := g.Group("/users/")
userGroup.Use(MiddlewareProvider.GetMiddleware(USER_MIDDLEWARE))
userGroup.GET(
"/info/:user-id",
userroutes.GetUserInfo,
)
// Admin group routes // Admin group routes
adminGroup := g.Group("/admin/") adminGroup := g.Group("/admin/")
adminGroup.Use(middleware.AdminMiddleware()) adminGroup.Use(middleware.AdminMiddleware())

View File

@ -0,0 +1,35 @@
package userroutes
import (
"context"
db_repo "enshi/db/go_queries"
"enshi/db_connection"
"enshi/middleware/getters"
"github.com/gin-gonic/gin"
)
func GetUserInfo(c *gin.Context) {
userId, err := getters.GetInt64Param(c, "user-id")
if err != nil {
c.JSON(400, gin.H{"error": "Invalid user ID"})
return
}
userInfo, err := db_repo.New(db_connection.Dbx).GetUserById(context.Background(), userId)
if err != nil {
c.JSON(500, gin.H{"error": "Failed to retrieve user information"})
return
}
userProfileInfo, err := db_repo.New(db_connection.Dbx).GetProfileByUserId(context.Background(), userId)
if err != nil {
c.JSON(500, gin.H{"error": "Failed to retrieve user profile information"})
return
}
c.JSON(200, gin.H{
"user_info": userInfo,
"profile_info": userProfileInfo,
})
}