mirror of
https://github.com/usatiuk/y.git
synced 2025-10-28 10:37:47 +01:00
somewhat working hater management
This commit is contained in:
@@ -37,6 +37,7 @@ import { Chats } from "./Chats";
|
||||
import { ChatCreate } from "./ChatCreate";
|
||||
import { Chat } from "./Chat";
|
||||
import { ChatEdit } from "./ChatEdit";
|
||||
import { Haters } from "./Haters";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@@ -98,6 +99,10 @@ const router = createBrowserRouter([
|
||||
// action: profileSelfAction,
|
||||
element: <Profile self={false} />,
|
||||
},
|
||||
{
|
||||
path: "haters",
|
||||
element: <Haters />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
21
client/src/Haters.scss
Normal file
21
client/src/Haters.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
@import "./common";
|
||||
|
||||
.hatersManagement {
|
||||
min-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2.5rem 2rem 0;
|
||||
|
||||
.tools {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 10rem;
|
||||
}
|
||||
|
||||
.found {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.posts {
|
||||
}
|
||||
}
|
||||
178
client/src/Haters.tsx
Normal file
178
client/src/Haters.tsx
Normal file
@@ -0,0 +1,178 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { deleteUser, getAllPerson } from "./api/Person";
|
||||
import { getAllMessage } from "./api/Message";
|
||||
import { getAllPost } from "./api/Post";
|
||||
import { isError, TMessageTo, TPersonTo, TPostTo } from "./api/dto";
|
||||
|
||||
import "./Haters.scss";
|
||||
import "./Post.scss";
|
||||
import { getTokenUserUuid } from "./api/utils";
|
||||
|
||||
export function Haters() {
|
||||
const [data, setData] = useState<
|
||||
| null
|
||||
| {
|
||||
error: false;
|
||||
persons: TPersonTo[];
|
||||
messages: TMessageTo[];
|
||||
posts: TPostTo[];
|
||||
}
|
||||
| { error: true; what: string }
|
||||
>(null);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
const [deleted, setDeleted] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
const fetchall = async () => {
|
||||
return {
|
||||
error: false,
|
||||
persons: await getAllPerson(),
|
||||
messages: await getAllMessage(),
|
||||
posts: await getAllPost(),
|
||||
};
|
||||
};
|
||||
fetchall()
|
||||
.then((result) => {
|
||||
if (isError(result.persons))
|
||||
setData({
|
||||
error: true,
|
||||
what: result.persons.errors.join(" "),
|
||||
});
|
||||
else if (isError(result.messages))
|
||||
setData({
|
||||
error: true,
|
||||
what: result.messages.errors.join(" "),
|
||||
});
|
||||
else if (isError(result.posts))
|
||||
setData({
|
||||
error: true,
|
||||
what: result.posts.errors.join(" "),
|
||||
});
|
||||
else if (!ignore) {
|
||||
setData(
|
||||
result as {
|
||||
error: false;
|
||||
persons: TPersonTo[];
|
||||
messages: TMessageTo[];
|
||||
posts: TPostTo[];
|
||||
},
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((e) => setData(e));
|
||||
return () => {
|
||||
ignore = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!data || data.error) {
|
||||
return (
|
||||
<div className={"hatersManagement"}>
|
||||
{data ? data.what : "error"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const foundM = data.messages.filter(
|
||||
(m) =>
|
||||
m.contents.includes(search) && m.authorUuid != getTokenUserUuid(),
|
||||
);
|
||||
const foundP = data.posts.filter(
|
||||
(p) => p.text.includes(search) && p.authorUuid != getTokenUserUuid(),
|
||||
);
|
||||
|
||||
const foundU = new Set<string>();
|
||||
|
||||
const allfound: {
|
||||
id: number;
|
||||
type: string;
|
||||
contents: string;
|
||||
author: string;
|
||||
}[] = [];
|
||||
|
||||
foundM.forEach((m) => {
|
||||
foundU.add(m.authorUuid);
|
||||
allfound.push({
|
||||
id: m.id,
|
||||
type: "message",
|
||||
contents: m.contents,
|
||||
author: m.authorUsername,
|
||||
});
|
||||
});
|
||||
foundP.forEach((p) => {
|
||||
foundU.add(p.authorUuid);
|
||||
allfound.push({
|
||||
id: p.id,
|
||||
type: "post",
|
||||
contents: p.text,
|
||||
author: p.authorUsername,
|
||||
});
|
||||
});
|
||||
|
||||
console.log(foundU);
|
||||
|
||||
const foundUArr = Array.from(foundU);
|
||||
|
||||
return (
|
||||
<div className={"hatersManagement"}>
|
||||
<div className={"tools"}>
|
||||
<label htmlFor="whattosearch">Search for:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="whattosearch"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
foundUArr.forEach((u) =>
|
||||
deleteUser(u)
|
||||
.then((e) => {
|
||||
if (isError(e)) {
|
||||
setDeleted((old) => [
|
||||
...old,
|
||||
`error deleting user with uuid ${u}: ` +
|
||||
e.errors.join(" "),
|
||||
]);
|
||||
} else {
|
||||
setDeleted((old) => [
|
||||
...old,
|
||||
"deleted user with id: " + u,
|
||||
]);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
setDeleted((old) => [
|
||||
...old,
|
||||
`error deleting user with uuid ${u}`,
|
||||
]);
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
delete all users
|
||||
</button>
|
||||
<div>
|
||||
{deleted.map((d) => (
|
||||
<span>{d}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={"found"}>
|
||||
{allfound.map((m) => (
|
||||
<div key={m.type + +m.id} className={"post"}>
|
||||
<span className={"text"}>{m.contents}</span>
|
||||
<div className={"footer"}>
|
||||
<div className={"info"}>
|
||||
{m.type} by {m.author}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -53,6 +53,14 @@ export function Home() {
|
||||
<NavLink to={"profile"} className={activePendingClassName}>
|
||||
Profile
|
||||
</NavLink>
|
||||
{loaderData.isAdmin && (
|
||||
<NavLink
|
||||
to={"haters"}
|
||||
className={activePendingClassName}
|
||||
>
|
||||
Haters
|
||||
</NavLink>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div id="HomeContent">
|
||||
|
||||
@@ -76,17 +76,25 @@ export function Profile({ self }: IProfileProps) {
|
||||
<>
|
||||
<span className={"fullName"}>{user.fullName}</span>
|
||||
<span className={"username"}>{user.username}</span>
|
||||
{<button onClick={() => setEditing(true)}>edit</button>}
|
||||
<Form className={"postForm"} method="post">
|
||||
<button
|
||||
name="intent"
|
||||
value="deleteSelf"
|
||||
type="submit"
|
||||
disabled={busy}
|
||||
>
|
||||
delete account
|
||||
</button>
|
||||
</Form>
|
||||
{self && (
|
||||
<>
|
||||
{
|
||||
<button onClick={() => setEditing(true)}>
|
||||
edit
|
||||
</button>
|
||||
}
|
||||
<Form className={"postForm"} method="post">
|
||||
<button
|
||||
name="intent"
|
||||
value="deleteSelf"
|
||||
type="submit"
|
||||
disabled={busy}
|
||||
>
|
||||
delete account
|
||||
</button>
|
||||
</Form>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -46,3 +46,7 @@ export async function deleteMessage(
|
||||
NoContentToResp,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getAllMessage(): Promise<TMessagesToResp> {
|
||||
return fetchJSONAuth("/message", "GET", MessagesToResp);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ export async function deleteSelf(): Promise<TNoContentToResp> {
|
||||
return fetchJSONAuth("/person/self", "DELETE", NoContentToResp);
|
||||
}
|
||||
|
||||
export async function deleteUser(uuid: string): Promise<TNoContentToResp> {
|
||||
return fetchJSONAuth("/person/by-uuid/" + uuid, "DELETE", NoContentToResp);
|
||||
}
|
||||
|
||||
export async function getPersonByUuid(uuid: string): Promise<TPersonToResp> {
|
||||
return fetchJSONAuth("/person/by-uuid/" + uuid, "GET", PersonToResp);
|
||||
}
|
||||
|
||||
@@ -50,3 +50,7 @@ export async function getPostsByAuthorUsername(
|
||||
PostToArrResp,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getAllPost(): Promise<TPostToArrResp> {
|
||||
return fetchJSONAuth("/post", "GET", PostToArrResp);
|
||||
}
|
||||
|
||||
@@ -5,17 +5,20 @@ import com.usatiuk.tjv.y.server.dto.MessageTo;
|
||||
import com.usatiuk.tjv.y.server.dto.converters.MessageMapper;
|
||||
import com.usatiuk.tjv.y.server.entity.Message;
|
||||
import com.usatiuk.tjv.y.server.entity.Person;
|
||||
import com.usatiuk.tjv.y.server.security.UserRoles;
|
||||
import com.usatiuk.tjv.y.server.service.ChatService;
|
||||
import com.usatiuk.tjv.y.server.service.MessageService;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/message", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@@ -75,4 +78,13 @@ public class MessageController {
|
||||
messageService.deleteById(id);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Stream<MessageTo> getAll(Authentication authentication) {
|
||||
if (!authentication.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
|
||||
return StreamSupport.stream(messageService.readAll().spliterator(), false).map(messageMapper::makeDto);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.usatiuk.tjv.y.server.dto.PersonTo;
|
||||
import com.usatiuk.tjv.y.server.dto.converters.PersonMapper;
|
||||
import com.usatiuk.tjv.y.server.entity.Chat;
|
||||
import com.usatiuk.tjv.y.server.entity.Person;
|
||||
import com.usatiuk.tjv.y.server.security.UserRoles;
|
||||
import com.usatiuk.tjv.y.server.service.ChatService;
|
||||
import com.usatiuk.tjv.y.server.service.PersonService;
|
||||
import com.usatiuk.tjv.y.server.service.exceptions.UserAlreadyExistsException;
|
||||
@@ -12,6 +13,7 @@ import com.usatiuk.tjv.y.server.service.exceptions.UserNotFoundException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
@@ -96,6 +98,26 @@ public class PersonController {
|
||||
personService.deleteById(authentication.getName());
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/by-uuid/{uuid}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void deleteByUuid(Authentication authentication, @PathVariable String uuid) throws UserNotFoundException {
|
||||
if (!authentication.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
|
||||
var person = personService.readById(uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
for (Chat c : person.getChats()) {
|
||||
c.getMembers().remove(person);
|
||||
chatService.update(c);
|
||||
}
|
||||
for (Person p : person.getFollowers()) {
|
||||
p.getFollowing().remove(person);
|
||||
personService.update(p);
|
||||
}
|
||||
|
||||
personService.deleteById(person.getUuid());
|
||||
}
|
||||
|
||||
|
||||
@GetMapping
|
||||
public Stream<PersonTo> getAll() throws UserNotFoundException {
|
||||
return StreamSupport.stream(personService.readAll().spliterator(), false).map(personMapper::makeDto);
|
||||
|
||||
@@ -5,17 +5,20 @@ import com.usatiuk.tjv.y.server.dto.PostTo;
|
||||
import com.usatiuk.tjv.y.server.dto.converters.PostMapper;
|
||||
import com.usatiuk.tjv.y.server.entity.Person;
|
||||
import com.usatiuk.tjv.y.server.entity.Post;
|
||||
import com.usatiuk.tjv.y.server.security.UserRoles;
|
||||
import com.usatiuk.tjv.y.server.service.PostService;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/post", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@@ -87,4 +90,12 @@ public class PostController {
|
||||
postService.deleteById(id);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Stream<PostTo> getAll(Authentication authentication) {
|
||||
if (!authentication.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
|
||||
return StreamSupport.stream(postService.readAll().spliterator(), false).map(postMapper::makeDto);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user