diff --git a/backend/src/file.ts b/backend/src/file.ts index 7e77582..35bdda4 100644 --- a/backend/src/file.ts +++ b/backend/src/file.ts @@ -74,4 +74,23 @@ router.get("/download", authenticate, (req, res) => { } }); +router.post("/delete", authenticate, (req, res) => { + const { room, filename } = req.body; + if (!room || !filename) { + return res.status(422).send("422 Unprocessable Entity: Missing room or filename"); + } + const dir = path.resolve(baseDir, room, filename); + try { + if (fs.existsSync(dir)) { + fs.unlinkSync(dir); + return res.send("File deleted successfully"); + } else { + return res.status(404).send("404 Not Found: File does not exist"); + } + } catch (err) { + console.error(err); + return res.sendStatus(500); + } +}) + export default router; diff --git a/backend/src/graphql.ts b/backend/src/graphql.ts index 8e65cb6..5036550 100644 --- a/backend/src/graphql.ts +++ b/backend/src/graphql.ts @@ -303,6 +303,10 @@ export type Mutation_Root = { delete_message?: Maybe; /** delete single row from the table: "message" */ delete_message_by_pk?: Maybe; + /** delete data from the table: "reply" */ + delete_reply?: Maybe; + /** delete single row from the table: "reply" */ + delete_reply_by_pk?: Maybe; /** delete data from the table: "room" */ delete_room?: Maybe; /** delete single row from the table: "room" */ @@ -319,6 +323,10 @@ export type Mutation_Root = { insert_message?: Maybe; /** insert a single row into the table: "message" */ insert_message_one?: Maybe; + /** insert data into the table: "reply" */ + insert_reply?: Maybe; + /** insert a single row into the table: "reply" */ + insert_reply_one?: Maybe; /** insert data into the table: "room" */ insert_room?: Maybe; /** insert a single row into the table: "room" */ @@ -337,6 +345,12 @@ export type Mutation_Root = { update_message_by_pk?: Maybe; /** update multiples rows of table: "message" */ update_message_many?: Maybe>>; + /** update data of the table: "reply" */ + update_reply?: Maybe; + /** update single row of the table: "reply" */ + update_reply_by_pk?: Maybe; + /** update multiples rows of table: "reply" */ + update_reply_many?: Maybe>>; /** update data of the table: "room" */ update_room?: Maybe; /** update single row of the table: "room" */ @@ -370,6 +384,18 @@ export type Mutation_RootDelete_Message_By_PkArgs = { }; +/** mutation root */ +export type Mutation_RootDelete_ReplyArgs = { + where: Reply_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootDelete_Reply_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + /** mutation root */ export type Mutation_RootDelete_RoomArgs = { where: Room_Bool_Exp; @@ -421,6 +447,20 @@ export type Mutation_RootInsert_Message_OneArgs = { }; +/** mutation root */ +export type Mutation_RootInsert_ReplyArgs = { + objects: Array; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type Mutation_RootInsert_Reply_OneArgs = { + object: Reply_Insert_Input; + on_conflict?: InputMaybe; +}; + + /** mutation root */ export type Mutation_RootInsert_RoomArgs = { objects: Array; @@ -483,6 +523,26 @@ export type Mutation_RootUpdate_Message_ManyArgs = { }; +/** mutation root */ +export type Mutation_RootUpdate_ReplyArgs = { + _set?: InputMaybe; + where: Reply_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_Reply_By_PkArgs = { + _set?: InputMaybe; + pk_columns: Reply_Pk_Columns_Input; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_Reply_ManyArgs = { + updates: Array; +}; + + /** mutation root */ export type Mutation_RootUpdate_RoomArgs = { _set?: InputMaybe; @@ -566,6 +626,12 @@ export type Query_Root = { message_aggregate: Message_Aggregate; /** fetch data from the table: "message" using primary key columns */ message_by_pk?: Maybe; + /** fetch data from the table: "reply" */ + reply: Array; + /** fetch aggregated fields from the table: "reply" */ + reply_aggregate: Reply_Aggregate; + /** fetch data from the table: "reply" using primary key columns */ + reply_by_pk?: Maybe; /** fetch data from the table: "room" */ room: Array; /** fetch aggregated fields from the table: "room" */ @@ -610,6 +676,29 @@ export type Query_RootMessage_By_PkArgs = { }; +export type Query_RootReplyArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootReply_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootReply_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + export type Query_RootRoomArgs = { distinct_on?: InputMaybe>; limit?: InputMaybe; @@ -679,6 +768,164 @@ export type Query_RootUser_Room_By_PkArgs = { user_uuid: Scalars['uuid']['input']; }; +/** columns and relationships of "reply" */ +export type Reply = { + __typename?: 'reply'; + content: Scalars['String']['output']; + msg_uuid?: Maybe; + user_uuid?: Maybe; + uuid: Scalars['uuid']['output']; +}; + +/** aggregated selection of "reply" */ +export type Reply_Aggregate = { + __typename?: 'reply_aggregate'; + aggregate?: Maybe; + nodes: Array; +}; + +/** aggregate fields of "reply" */ +export type Reply_Aggregate_Fields = { + __typename?: 'reply_aggregate_fields'; + count: Scalars['Int']['output']; + max?: Maybe; + min?: Maybe; +}; + + +/** aggregate fields of "reply" */ +export type Reply_Aggregate_FieldsCountArgs = { + columns?: InputMaybe>; + distinct?: InputMaybe; +}; + +/** Boolean expression to filter rows from the table "reply". All fields are combined with a logical 'AND'. */ +export type Reply_Bool_Exp = { + _and?: InputMaybe>; + _not?: InputMaybe; + _or?: InputMaybe>; + content?: InputMaybe; + msg_uuid?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** unique or primary key constraints on table "reply" */ +export enum Reply_Constraint { + /** unique or primary key constraint on columns "uuid" */ + ReplyPkey = 'reply_pkey' +} + +/** input type for inserting data into table "reply" */ +export type Reply_Insert_Input = { + content?: InputMaybe; + msg_uuid?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** aggregate max on columns */ +export type Reply_Max_Fields = { + __typename?: 'reply_max_fields'; + content?: Maybe; + msg_uuid?: Maybe; + user_uuid?: Maybe; + uuid?: Maybe; +}; + +/** aggregate min on columns */ +export type Reply_Min_Fields = { + __typename?: 'reply_min_fields'; + content?: Maybe; + msg_uuid?: Maybe; + user_uuid?: Maybe; + uuid?: Maybe; +}; + +/** response of any mutation on the table "reply" */ +export type Reply_Mutation_Response = { + __typename?: 'reply_mutation_response'; + /** number of rows affected by the mutation */ + affected_rows: Scalars['Int']['output']; + /** data from the rows affected by the mutation */ + returning: Array; +}; + +/** on_conflict condition type for table "reply" */ +export type Reply_On_Conflict = { + constraint: Reply_Constraint; + update_columns?: Array; + where?: InputMaybe; +}; + +/** Ordering options when selecting data from "reply". */ +export type Reply_Order_By = { + content?: InputMaybe; + msg_uuid?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** primary key columns input for table: reply */ +export type Reply_Pk_Columns_Input = { + uuid: Scalars['uuid']['input']; +}; + +/** select columns of table "reply" */ +export enum Reply_Select_Column { + /** column name */ + Content = 'content', + /** column name */ + MsgUuid = 'msg_uuid', + /** column name */ + UserUuid = 'user_uuid', + /** column name */ + Uuid = 'uuid' +} + +/** input type for updating data in table "reply" */ +export type Reply_Set_Input = { + content?: InputMaybe; + msg_uuid?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** Streaming cursor of the table "reply" */ +export type Reply_Stream_Cursor_Input = { + /** Stream column input with initial value */ + initial_value: Reply_Stream_Cursor_Value_Input; + /** cursor ordering */ + ordering?: InputMaybe; +}; + +/** Initial value of the column from where the streaming should start */ +export type Reply_Stream_Cursor_Value_Input = { + content?: InputMaybe; + msg_uuid?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** update columns of table "reply" */ +export enum Reply_Update_Column { + /** column name */ + Content = 'content', + /** column name */ + MsgUuid = 'msg_uuid', + /** column name */ + UserUuid = 'user_uuid', + /** column name */ + Uuid = 'uuid' +} + +export type Reply_Updates = { + /** sets the columns of the filtered rows to the given values */ + _set?: InputMaybe; + /** filter the rows which have to be updated */ + where: Reply_Bool_Exp; +}; + /** columns and relationships of "room" */ export type Room = { __typename?: 'room'; @@ -926,6 +1173,14 @@ export type Subscription_Root = { message_by_pk?: Maybe; /** fetch data from the table in a streaming manner: "message" */ message_stream: Array; + /** fetch data from the table: "reply" */ + reply: Array; + /** fetch aggregated fields from the table: "reply" */ + reply_aggregate: Reply_Aggregate; + /** fetch data from the table: "reply" using primary key columns */ + reply_by_pk?: Maybe; + /** fetch data from the table in a streaming manner: "reply" */ + reply_stream: Array; /** fetch data from the table: "room" */ room: Array; /** fetch aggregated fields from the table: "room" */ @@ -983,6 +1238,36 @@ export type Subscription_RootMessage_StreamArgs = { }; +export type Subscription_RootReplyArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootReply_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootReply_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +export type Subscription_RootReply_StreamArgs = { + batch_size: Scalars['Int']['input']; + cursor: Array>; + where?: InputMaybe; +}; + + export type Subscription_RootRoomArgs = { distinct_on?: InputMaybe>; limit?: InputMaybe; @@ -1319,7 +1604,7 @@ export type User_Room_Bool_Exp = { /** unique or primary key constraints on table "user_room" */ export enum User_Room_Constraint { - /** unique or primary key constraint on columns "user_uuid", "room_uuid" */ + /** unique or primary key constraint on columns "room_uuid", "user_uuid" */ UserRoomPkey = 'user_room_pkey' } @@ -1508,6 +1793,13 @@ export type GetMessagesByRoomSubscriptionVariables = Exact<{ export type GetMessagesByRoomSubscription = { __typename?: 'subscription_root', message: Array<{ __typename?: 'message', uuid: any, content: string, created_at: any, user: { __typename?: 'user', uuid: any, username: string } }> }; +export type GetMessagesByUserSubscriptionVariables = Exact<{ + user_uuid: Scalars['uuid']['input']; +}>; + + +export type GetMessagesByUserSubscription = { __typename?: 'subscription_root', message: Array<{ __typename?: 'message', uuid: any, content: string, created_at: any, room: { __typename?: 'room', uuid: any } }> }; + export type AddRoomMutationVariables = Exact<{ name: Scalars['String']['input']; intro: Scalars['String']['input']; @@ -1539,6 +1831,14 @@ export type JoinRoomMutationVariables = Exact<{ export type JoinRoomMutation = { __typename?: 'mutation_root', insert_user_room_one?: { __typename?: 'user_room', user_uuid: any, room_uuid: any } | null }; +export type QuitRoomMutationVariables = Exact<{ + user_uuid: Scalars['uuid']['input']; + room_uuid: Scalars['uuid']['input']; +}>; + + +export type QuitRoomMutation = { __typename?: 'mutation_root', delete_user_room?: { __typename?: 'user_room_mutation_response', affected_rows: number } | null }; + export type AddUserMutationVariables = Exact<{ username: Scalars['String']['input']; password: Scalars['String']['input']; @@ -1554,6 +1854,13 @@ export type GetUsersByUsernameQueryVariables = Exact<{ export type GetUsersByUsernameQuery = { __typename?: 'query_root', user: Array<{ __typename?: 'user', uuid: any, password: string }> }; +export type DeleteUserMutationVariables = Exact<{ + uuid: Scalars['uuid']['input']; +}>; + + +export type DeleteUserMutation = { __typename?: 'mutation_root', delete_user_by_pk?: { __typename?: 'user', uuid: any } | null }; + export const AddMessageDocument = gql` mutation addMessage($user_uuid: uuid!, $room_uuid: uuid!, $content: String!) { @@ -1577,6 +1884,18 @@ export const GetMessagesByRoomDocument = gql` } } `; +export const GetMessagesByUserDocument = gql` + subscription getMessagesByUser($user_uuid: uuid!) { + message(where: {user_uuid: {_eq: $user_uuid}}) { + uuid + room { + uuid + } + content + created_at + } +} + `; export const AddRoomDocument = gql` mutation addRoom($name: String!, $intro: String!, $invite_code: String!) { insert_room_one(object: {name: $name, intro: $intro, invite_code: $invite_code}) { @@ -1612,6 +1931,15 @@ export const JoinRoomDocument = gql` } } `; +export const QuitRoomDocument = gql` + mutation quitRoom($user_uuid: uuid!, $room_uuid: uuid!) { + delete_user_room( + where: {user_uuid: {_eq: $user_uuid}, room_uuid: {_eq: $room_uuid}} + ) { + affected_rows + } +} + `; export const AddUserDocument = gql` mutation addUser($username: String!, $password: String!) { insert_user_one(object: {username: $username, password: $password}) { @@ -1627,6 +1955,13 @@ export const GetUsersByUsernameDocument = gql` } } `; +export const DeleteUserDocument = gql` + mutation deleteUser($uuid: uuid!) { + delete_user_by_pk(uuid: $uuid) { + uuid + } +} + `; export type SdkFunctionWrapper = (action: (requestHeaders?:Record) => Promise, operationName: string, operationType?: string, variables?: any) => Promise; @@ -1641,6 +1976,9 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = getMessagesByRoom(variables: GetMessagesByRoomSubscriptionVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { return withWrapper((wrappedRequestHeaders) => client.request(GetMessagesByRoomDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'getMessagesByRoom', 'subscription', variables); }, + getMessagesByUser(variables: GetMessagesByUserSubscriptionVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(GetMessagesByUserDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'getMessagesByUser', 'subscription', variables); + }, addRoom(variables: AddRoomMutationVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { return withWrapper((wrappedRequestHeaders) => client.request(AddRoomDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'addRoom', 'mutation', variables); }, @@ -1653,12 +1991,18 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = joinRoom(variables: JoinRoomMutationVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { return withWrapper((wrappedRequestHeaders) => client.request(JoinRoomDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'joinRoom', 'mutation', variables); }, + quitRoom(variables: QuitRoomMutationVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(QuitRoomDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'quitRoom', 'mutation', variables); + }, addUser(variables: AddUserMutationVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { return withWrapper((wrappedRequestHeaders) => client.request(AddUserDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'addUser', 'mutation', variables); }, getUsersByUsername(variables: GetUsersByUsernameQueryVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { return withWrapper((wrappedRequestHeaders) => client.request(GetUsersByUsernameDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'getUsersByUsername', 'query', variables); + }, + deleteUser(variables: DeleteUserMutationVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(DeleteUserDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'deleteUser', 'mutation', variables); } }; } -export type Sdk = ReturnType; +export type Sdk = ReturnType; \ No newline at end of file diff --git a/backend/src/user.ts b/backend/src/user.ts index a93bd91..7547e9c 100644 --- a/backend/src/user.ts +++ b/backend/src/user.ts @@ -1,6 +1,7 @@ import express from "express"; import jwt from "jsonwebtoken"; import { sdk as graphql } from "./index"; +import authenticate from "./authenticate"; interface userJWTPayload { uuid: string; @@ -71,4 +72,20 @@ router.post("/register", async (req, res) => { } }); +router.get("/delete", authenticate, (req, res) => { + const authHeader = req.get("Authorization"); + const token = authHeader!.substring(7); + const decoded = jwt.decode(token) as userJWTPayload; + const uuid = decoded?.uuid; + if (!uuid) { + return res.status(422).send("422 Unprocessable Entity: Missing uuid"); + } + graphql.deleteUser({ uuid: uuid as string }).then(() => { + return res.sendStatus(200); + }).catch((err) => { + console.error(err); + return res.sendStatus(500); + }); +}) + export default router; diff --git a/database/.local.env.template b/database/.local.env.template index 89074f3..992be1d 100644 --- a/database/.local.env.template +++ b/database/.local.env.template @@ -1,2 +1,2 @@ HASURA_GRAPHQL_ENDPOINT=
:/v1/graphql -HASURA_GRAPHQL_ADMIN_SECRET= +tHASURA_GRAPHQL_ADMIN_SECRET= diff --git a/database/graphql/message.graphql b/database/graphql/message.graphql index 994647c..40c0f2c 100644 --- a/database/graphql/message.graphql +++ b/database/graphql/message.graphql @@ -15,3 +15,14 @@ subscription getMessagesByRoom($room_uuid: uuid!) { created_at } } + +subscription getMessagesByUser($user_uuid: uuid!) { + message(where: {user_uuid: {_eq: $user_uuid}}) { + uuid + room { + uuid + } + content + created_at + } +} \ No newline at end of file diff --git a/database/graphql/reply.graphql b/database/graphql/reply.graphql new file mode 100644 index 0000000..56962bc --- /dev/null +++ b/database/graphql/reply.graphql @@ -0,0 +1,17 @@ +mutation addReply($user_uuid: uuid!, $msg_uuid: uuid!, $content: String!) { + insert_reply_one(object: {user_uuid: $user_uuid, msg_uuid: $msg_uuid, content: $content}) { + uuid + } +} + +subscription getReplyByMessage($msg_uuid: uuid!) { + reply(where: {msg_uuid: {_eq: $msg_uuid}}) { + uuid + user { + uuid + username + } + content + created_at + } +} \ No newline at end of file diff --git a/database/graphql/room.graphql b/database/graphql/room.graphql index 4f37de0..3b61404 100644 --- a/database/graphql/room.graphql +++ b/database/graphql/room.graphql @@ -28,3 +28,9 @@ mutation joinRoom($user_uuid: uuid!, $room_uuid: uuid!) { room_uuid } } + +mutation quitRoom($user_uuid: uuid!, $room_uuid: uuid!) { + delete_user_room(where: {user_uuid: {_eq: $user_uuid}, room_uuid: {_eq: $room_uuid}}) { + affected_rows + } +} \ No newline at end of file diff --git a/database/graphql/user.graphql b/database/graphql/user.graphql index d7780cd..bc1a511 100644 --- a/database/graphql/user.graphql +++ b/database/graphql/user.graphql @@ -10,3 +10,9 @@ query getUsersByUsername($username: String!) { password } } + +mutation deleteUser($uuid: uuid!) { + delete_user_by_pk(uuid: $uuid) { + uuid + } +} \ No newline at end of file diff --git a/database/hasura/hasura_metadata.json b/database/hasura/hasura_metadata.json index 8c4ac68..703633b 100644 --- a/database/hasura/hasura_metadata.json +++ b/database/hasura/hasura_metadata.json @@ -1,5 +1,5 @@ { - "resource_version": 14, + "resource_version": 17, "metadata": { "version": 3, "sources": [ @@ -85,6 +85,67 @@ } ] }, + { + "table": { + "name": "reply", + "schema": "public" + }, + "insert_permissions": [ + { + "role": "user", + "permission": { + "check": {}, + "columns": [ + "content", + "msg_uuid", + "user_uuid", + "uuid" + ] + }, + "comment": "" + } + ], + "select_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "content", + "msg_uuid", + "user_uuid", + "uuid" + ], + "filter": {} + }, + "comment": "" + } + ], + "update_permissions": [ + { + "role": "user", + "permission": { + "columns": [ + "content", + "msg_uuid", + "user_uuid", + "uuid" + ], + "filter": {}, + "check": null + }, + "comment": "" + } + ], + "delete_permissions": [ + { + "role": "user", + "permission": { + "filter": {} + }, + "comment": "" + } + ] + }, { "table": { "name": "room",