mirror of
https://github.com/usatiuk/y.git
synced 2025-10-28 18: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 { ChatCreate } from "./ChatCreate";
|
||||||
import { Chat } from "./Chat";
|
import { Chat } from "./Chat";
|
||||||
import { ChatEdit } from "./ChatEdit";
|
import { ChatEdit } from "./ChatEdit";
|
||||||
|
import { Haters } from "./Haters";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@@ -98,6 +99,10 @@ const router = createBrowserRouter([
|
|||||||
// action: profileSelfAction,
|
// action: profileSelfAction,
|
||||||
element: <Profile self={false} />,
|
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}>
|
<NavLink to={"profile"} className={activePendingClassName}>
|
||||||
Profile
|
Profile
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
{loaderData.isAdmin && (
|
||||||
|
<NavLink
|
||||||
|
to={"haters"}
|
||||||
|
className={activePendingClassName}
|
||||||
|
>
|
||||||
|
Haters
|
||||||
|
</NavLink>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="HomeContent">
|
<div id="HomeContent">
|
||||||
|
|||||||
@@ -76,17 +76,25 @@ export function Profile({ self }: IProfileProps) {
|
|||||||
<>
|
<>
|
||||||
<span className={"fullName"}>{user.fullName}</span>
|
<span className={"fullName"}>{user.fullName}</span>
|
||||||
<span className={"username"}>{user.username}</span>
|
<span className={"username"}>{user.username}</span>
|
||||||
{<button onClick={() => setEditing(true)}>edit</button>}
|
{self && (
|
||||||
<Form className={"postForm"} method="post">
|
<>
|
||||||
<button
|
{
|
||||||
name="intent"
|
<button onClick={() => setEditing(true)}>
|
||||||
value="deleteSelf"
|
edit
|
||||||
type="submit"
|
</button>
|
||||||
disabled={busy}
|
}
|
||||||
>
|
<Form className={"postForm"} method="post">
|
||||||
delete account
|
<button
|
||||||
</button>
|
name="intent"
|
||||||
</Form>
|
value="deleteSelf"
|
||||||
|
type="submit"
|
||||||
|
disabled={busy}
|
||||||
|
>
|
||||||
|
delete account
|
||||||
|
</button>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -46,3 +46,7 @@ export async function deleteMessage(
|
|||||||
NoContentToResp,
|
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);
|
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> {
|
export async function getPersonByUuid(uuid: string): Promise<TPersonToResp> {
|
||||||
return fetchJSONAuth("/person/by-uuid/" + uuid, "GET", PersonToResp);
|
return fetchJSONAuth("/person/by-uuid/" + uuid, "GET", PersonToResp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,3 +50,7 @@ export async function getPostsByAuthorUsername(
|
|||||||
PostToArrResp,
|
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.dto.converters.MessageMapper;
|
||||||
import com.usatiuk.tjv.y.server.entity.Message;
|
import com.usatiuk.tjv.y.server.entity.Message;
|
||||||
import com.usatiuk.tjv.y.server.entity.Person;
|
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.ChatService;
|
||||||
import com.usatiuk.tjv.y.server.service.MessageService;
|
import com.usatiuk.tjv.y.server.service.MessageService;
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(value = "/message", produces = MediaType.APPLICATION_JSON_VALUE)
|
@RequestMapping(value = "/message", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
@@ -75,4 +78,13 @@ public class MessageController {
|
|||||||
messageService.deleteById(id);
|
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.dto.converters.PersonMapper;
|
||||||
import com.usatiuk.tjv.y.server.entity.Chat;
|
import com.usatiuk.tjv.y.server.entity.Chat;
|
||||||
import com.usatiuk.tjv.y.server.entity.Person;
|
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.ChatService;
|
||||||
import com.usatiuk.tjv.y.server.service.PersonService;
|
import com.usatiuk.tjv.y.server.service.PersonService;
|
||||||
import com.usatiuk.tjv.y.server.service.exceptions.UserAlreadyExistsException;
|
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.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
@@ -96,6 +98,26 @@ public class PersonController {
|
|||||||
personService.deleteById(authentication.getName());
|
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
|
@GetMapping
|
||||||
public Stream<PersonTo> getAll() throws UserNotFoundException {
|
public Stream<PersonTo> getAll() throws UserNotFoundException {
|
||||||
return StreamSupport.stream(personService.readAll().spliterator(), false).map(personMapper::makeDto);
|
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.dto.converters.PostMapper;
|
||||||
import com.usatiuk.tjv.y.server.entity.Person;
|
import com.usatiuk.tjv.y.server.entity.Person;
|
||||||
import com.usatiuk.tjv.y.server.entity.Post;
|
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 com.usatiuk.tjv.y.server.service.PostService;
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(value = "/post", produces = MediaType.APPLICATION_JSON_VALUE)
|
@RequestMapping(value = "/post", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
@@ -87,4 +90,12 @@ public class PostController {
|
|||||||
postService.deleteById(id);
|
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