From e649bde0576a15d662d1c46683125e6fa1bb6b5f Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 8 Nov 2022 16:23:09 +0900 Subject: [PATCH 001/308] Add: rooms.js --- test/rooms.js | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 test/rooms.js diff --git a/test/rooms.js b/test/rooms.js new file mode 100644 index 00000000..52dcdf5e --- /dev/null +++ b/test/rooms.js @@ -0,0 +1,111 @@ +const request = require("supertest"); +const expect = require("chai").expect; +const express = require("express"); + +const authHandlers = require("../src/service/auth.replace"); +const roomsHandlers = require("../src/service/rooms"); +const { userModel, roomModel, locationModel } = require("../src/db/mongo"); + +const security = require("../security"); + +describe("room createHandler", function () { + it("should create room correctly", async function () { + let testUser = await userModel.findOne({ id: "sunday" }); + let testFrom = await locationModel.findOne({ koName: "대전역" }); + let testTo = await locationModel.findOne({ koName: "택시승강장" }); + let app = express(); + const req = { + body: { + name: "test-room", + from: testFrom._id, + to: testTo._id, + time: Date.now(), + maxPartLength: 4, + }, + userId: testUser.id, + app, + }; + const res = { + send: (data) => { + expect(data).to.has.property("name", "test-room"); + }, + status: (data) => { + expect(data).to.equal(200); + }, + }; + + await roomsHandlers.createHandler(req, res); + }); +}); + +describe("room infoHandler", function () { + it("should return information of room correctly", async function () { + let testUser = await userModel.findOne({ id: "sunday" }); + let room = await roomModel.findOne({ name: "test-room" }); + const req = { + query: { id: room._id }, + userId: testUser.id, + }; + const res = { + send: (data) => { + expect(data).to.has.property("name", "test-room"); + }, + status: (data) => { + expect(data).to.equal(200); + }, + }; + + await roomsHandlers.infoHandler(req, res); + }); +}); + +describe("room joinHandler", function () { + it("should return information of room and join successfully", async function () { + let testUser = await userModel.findOne({ id: "monday" }); + let testRoom = await roomModel.findOne({ name: "test-room" }); + let app = express(); + const req = { + body: { + roomId: testRoom._id, + }, + userId: testUser.id, + app, + }; + const res = { + send: (data) => { + expect(data).to.has.property("name", "test-room"); + }, + status: (data) => { + expect(data).to.equal(200); + }, + }; + + await roomsHandlers.joinHandler(req, res); + }); +}); + +describe("room searchHandler", function () { + it("should return information of room successfully", async function () { + let testFrom = await locationModel.findOne({ koName: "대전역" }); + let testTo = await locationModel.findOne({ koName: "택시승강장" }); + const req = { + query: { + name: "test-room", + from: testFrom._id, + to: testTo._id, + time: Date.now(), + maxPartLength: 4, + }, + }; + const res = { + json: (data) => { + console.log(data); + }, + status: (data) => { + expect(data).to.equal(200); + }, + }; + + await roomsHandlers.searchHandler(req, res); + }); +}); From 1ad39bb30f692f0bfbb50f4ead94badcd891ab0a Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 8 Nov 2022 16:25:56 +0900 Subject: [PATCH 002/308] Fix: rooms.js --- test/rooms.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index 52dcdf5e..1a8a683e 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -98,9 +98,6 @@ describe("room searchHandler", function () { }, }; const res = { - json: (data) => { - console.log(data); - }, status: (data) => { expect(data).to.equal(200); }, From d8e18fb15134d14ef267b4b3f72c843d1787e064 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 8 Nov 2022 16:33:05 +0900 Subject: [PATCH 003/308] Fix: rooms.js --- test/rooms.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index 1a8a683e..1db8bf48 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -84,25 +84,25 @@ describe("room joinHandler", function () { }); }); -describe("room searchHandler", function () { - it("should return information of room successfully", async function () { - let testFrom = await locationModel.findOne({ koName: "대전역" }); - let testTo = await locationModel.findOne({ koName: "택시승강장" }); - const req = { - query: { - name: "test-room", - from: testFrom._id, - to: testTo._id, - time: Date.now(), - maxPartLength: 4, - }, - }; - const res = { - status: (data) => { - expect(data).to.equal(200); - }, - }; +// describe("room searchHandler", function () { +// it("should return information of room successfully", async function () { +// let testFrom = await locationModel.findOne({ koName: "대전역" }); +// let testTo = await locationModel.findOne({ koName: "택시승강장" }); +// const req = { +// query: { +// name: "test-room", +// from: testFrom._id, +// to: testTo._id, +// time: Date.now(), +// maxPartLength: 4, +// }, +// }; +// const res = { +// status: (data) => { +// expect(data).to.equal(200); +// }, +// }; - await roomsHandlers.searchHandler(req, res); - }); -}); +// await roomsHandlers.searchHandler(req, res); +// }); +// }); From f5d730c335c3b5d84ffde87cec1d5e7894d8c9c1 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 8 Nov 2022 17:39:16 +0900 Subject: [PATCH 004/308] Refactor: fix and add more testcases --- test/rooms.js | 119 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 95 insertions(+), 24 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index 1db8bf48..0c99a39c 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -9,7 +9,7 @@ const { userModel, roomModel, locationModel } = require("../src/db/mongo"); const security = require("../security"); describe("room createHandler", function () { - it("should create room correctly", async function () { + it("should create room", async function () { let testUser = await userModel.findOne({ id: "sunday" }); let testFrom = await locationModel.findOne({ koName: "대전역" }); let testTo = await locationModel.findOne({ koName: "택시승강장" }); @@ -39,7 +39,7 @@ describe("room createHandler", function () { }); describe("room infoHandler", function () { - it("should return information of room correctly", async function () { + it("should return information of room", async function () { let testUser = await userModel.findOne({ id: "sunday" }); let room = await roomModel.findOne({ name: "test-room" }); const req = { @@ -49,6 +49,7 @@ describe("room infoHandler", function () { const res = { send: (data) => { expect(data).to.has.property("name", "test-room"); + expect(data).to.has.property("isOver"); }, status: (data) => { expect(data).to.equal(200); @@ -60,7 +61,7 @@ describe("room infoHandler", function () { }); describe("room joinHandler", function () { - it("should return information of room and join successfully", async function () { + it("should return information of room and join", async function () { let testUser = await userModel.findOne({ id: "monday" }); let testRoom = await roomModel.findOne({ name: "test-room" }); let app = express(); @@ -74,6 +75,7 @@ describe("room joinHandler", function () { const res = { send: (data) => { expect(data).to.has.property("name", "test-room"); + expect(data.part).to.have.lengthOf(2); }, status: (data) => { expect(data).to.equal(200); @@ -84,25 +86,94 @@ describe("room joinHandler", function () { }); }); -// describe("room searchHandler", function () { -// it("should return information of room successfully", async function () { -// let testFrom = await locationModel.findOne({ koName: "대전역" }); -// let testTo = await locationModel.findOne({ koName: "택시승강장" }); -// const req = { -// query: { -// name: "test-room", -// from: testFrom._id, -// to: testTo._id, -// time: Date.now(), -// maxPartLength: 4, -// }, -// }; -// const res = { -// status: (data) => { -// expect(data).to.equal(200); -// }, -// }; +describe("room searchHandler", function () { + it("should return information of searching room", async function () { + let testFrom = await locationModel.findOne({ koName: "대전역" }); + let testTo = await locationModel.findOne({ koName: "택시승강장" }); + const req = { + query: { + name: "test-room", + from: testFrom._id, + to: testTo._id, + time: Date.now() - 1000, + maxPartLength: 4, + }, + }; + const res = { + json: (data) => { + expect(data[0]).to.has.property("name", "test-room"); + expect(data[0]).to.has.property("settlementTotal"); + }, + status: (data) => { + expect(data).to.equal(200); + }, + }; + + await roomsHandlers.searchHandler(req, res); + }); +}); + +describe("room searchByUserHandler", function () { + it("should return information of searching room", async function () { + let testUser = await userModel.findOne({ id: "sunday" }); + const req = { + userId: testUser.id, + }; + const res = { + json: (data) => { + expect(data).to.has.property("ongoing"); + expect(data).to.has.property("done"); + }, + status: (data) => { + expect(data).to.equal(200); + }, + }; + + await roomsHandlers.searchByUserHandler(req, res); + }); +}); -// await roomsHandlers.searchHandler(req, res); -// }); -// }); +describe("room commitPaymentHandler", function () { + it("should return information of room and commit payment", async function () { + let testUser = await userModel.findOne({ id: "sunday" }); + let testRoom = await roomModel.findOne({ name: "test-room" }); + const req = { + body: { roomId: testRoom._id }, + userId: testUser.id, + timestamp: Date.now(), + }; + const res = { + send: (data) => { + expect(data).to.has.property("name", "test-room"); + expect(data).to.has.property("isOver", true); + expect(data).to.has.property("settlementTotal", 1); + }, + status: (data) => { + expect(data).to.equal(200); + }, + }; + await roomsHandlers.commitPaymentHandler(req, res); + }); +}); + +describe("room settlementHandler", function () { + it("should return information of room and set settlement", async function () { + let testUser = await userModel.findOne({ id: "monday" }); + let testRoom = await roomModel.findOne({ name: "test-room" }); + const req = { + body: { roomId: testRoom._id }, + userId: testUser.id, + }; + const res = { + send: (data) => { + expect(data).to.has.property("name", "test-room"); + expect(data).to.has.property("isOver", true); + expect(data).to.has.property("settlementTotal", 2); + }, + status: (data) => { + expect(data).to.equal(200); + }, + }; + await roomsHandlers.settlementHandler(req, res); + }); +}); From 116ed52af080b0a58c747fb723a6cccf3191f905 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 8 Nov 2022 18:47:08 +0900 Subject: [PATCH 005/308] Add: rooms.js testcase for abortHandler --- test/rooms.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index 0c99a39c..1dccca66 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -1,4 +1,3 @@ -const request = require("supertest"); const expect = require("chai").expect; const express = require("express"); @@ -6,8 +5,6 @@ const authHandlers = require("../src/service/auth.replace"); const roomsHandlers = require("../src/service/rooms"); const { userModel, roomModel, locationModel } = require("../src/db/mongo"); -const security = require("../security"); - describe("room createHandler", function () { it("should create room", async function () { let testUser = await userModel.findOne({ id: "sunday" }); @@ -177,3 +174,27 @@ describe("room settlementHandler", function () { await roomsHandlers.settlementHandler(req, res); }); }); + +describe("room abortHandler", function () { + it("should return information of room and abort user", async function () { + let testUser = await userModel.findOne({ id: "monday" }); + let testRoom = await roomModel.findOne({ name: "test-room" }); + let app = express(); + const req = { + body: { roomId: testRoom._id }, + userId: testUser.id, + session: {}, + app, + }; + const res = { + send: (data) => { + expect(data).to.has.property("name", "test-room"); + expect(data.part).to.have.lengthOf(1); + }, + status: (data) => { + expect(data).to.equal(200); + }, + }; + await roomsHandlers.abortHandler(req, res); + }); +}); From a17c999348917aee679e25f70c9a246b3c55dab3 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 9 Nov 2022 03:14:42 +0900 Subject: [PATCH 006/308] Test: refactor testcases with testdata --- test/rooms.js | 80 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index 1dccca66..2c89cfd0 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -1,16 +1,16 @@ const expect = require("chai").expect; const express = require("express"); - -const authHandlers = require("../src/service/auth.replace"); const roomsHandlers = require("../src/service/rooms"); const { userModel, roomModel, locationModel } = require("../src/db/mongo"); +const { generateProfileImageUrl } = require("../src/modules/modifyProfile"); +const app = express(); -describe("room createHandler", function () { +describe("[rooms] 1.createHandler", function () { it("should create room", async function () { - let testUser = await userModel.findOne({ id: "sunday" }); + let testUser1 = userGenerator("test1"); + await testUser1.save(); let testFrom = await locationModel.findOne({ koName: "대전역" }); let testTo = await locationModel.findOne({ koName: "택시승강장" }); - let app = express(); const req = { body: { name: "test-room", @@ -19,7 +19,7 @@ describe("room createHandler", function () { time: Date.now(), maxPartLength: 4, }, - userId: testUser.id, + userId: testUser1.id, app, }; const res = { @@ -35,13 +35,13 @@ describe("room createHandler", function () { }); }); -describe("room infoHandler", function () { +describe("[rooms] 2.infoHandler", function () { it("should return information of room", async function () { - let testUser = await userModel.findOne({ id: "sunday" }); + let testUser1 = await userModel.findOne({ id: "test1" }); let room = await roomModel.findOne({ name: "test-room" }); const req = { query: { id: room._id }, - userId: testUser.id, + userId: testUser1.id, }; const res = { send: (data) => { @@ -57,16 +57,16 @@ describe("room infoHandler", function () { }); }); -describe("room joinHandler", function () { +describe("[rooms] 3.joinHandler", function () { it("should return information of room and join", async function () { - let testUser = await userModel.findOne({ id: "monday" }); + let testUser2 = userGenerator("test2"); + await testUser2.save(); let testRoom = await roomModel.findOne({ name: "test-room" }); - let app = express(); const req = { body: { roomId: testRoom._id, }, - userId: testUser.id, + userId: testUser2.id, app, }; const res = { @@ -83,7 +83,7 @@ describe("room joinHandler", function () { }); }); -describe("room searchHandler", function () { +describe("[rooms] 4.searchHandler", function () { it("should return information of searching room", async function () { let testFrom = await locationModel.findOne({ koName: "대전역" }); let testTo = await locationModel.findOne({ koName: "택시승강장" }); @@ -110,11 +110,11 @@ describe("room searchHandler", function () { }); }); -describe("room searchByUserHandler", function () { +describe("[rooms] 5.searchByUserHandler", function () { it("should return information of searching room", async function () { - let testUser = await userModel.findOne({ id: "sunday" }); + let testUser1 = await userModel.findOne({ id: "test1" }); const req = { - userId: testUser.id, + userId: testUser1.id, }; const res = { json: (data) => { @@ -130,13 +130,13 @@ describe("room searchByUserHandler", function () { }); }); -describe("room commitPaymentHandler", function () { +describe("[rooms] 6.commitPaymentHandler", function () { it("should return information of room and commit payment", async function () { - let testUser = await userModel.findOne({ id: "sunday" }); + let testUser1 = await userModel.findOne({ id: "test1" }); let testRoom = await roomModel.findOne({ name: "test-room" }); const req = { body: { roomId: testRoom._id }, - userId: testUser.id, + userId: testUser1.id, timestamp: Date.now(), }; const res = { @@ -153,13 +153,13 @@ describe("room commitPaymentHandler", function () { }); }); -describe("room settlementHandler", function () { +describe("[rooms] 7.settlementHandler", function () { it("should return information of room and set settlement", async function () { - let testUser = await userModel.findOne({ id: "monday" }); + let testUser2 = await userModel.findOne({ id: "test2" }); let testRoom = await roomModel.findOne({ name: "test-room" }); const req = { body: { roomId: testRoom._id }, - userId: testUser.id, + userId: testUser2.id, }; const res = { send: (data) => { @@ -175,14 +175,19 @@ describe("room settlementHandler", function () { }); }); -describe("room abortHandler", function () { +describe("[rooms] 8.abortHandler", function () { + const removeTestData = async () => { + // drop all testData + await roomModel.deleteOne({ name: "test-room" }); + await userModel.deleteOne({ id: "test1" }); + await userModel.deleteOne({ id: "test2" }); + }; it("should return information of room and abort user", async function () { - let testUser = await userModel.findOne({ id: "monday" }); + let testUser2 = await userModel.findOne({ id: "test2" }); let testRoom = await roomModel.findOne({ name: "test-room" }); - let app = express(); const req = { body: { roomId: testRoom._id }, - userId: testUser.id, + userId: testUser2.id, session: {}, app, }; @@ -196,5 +201,26 @@ describe("room abortHandler", function () { }, }; await roomsHandlers.abortHandler(req, res); + after(removeTestData); }); }); + +// 테스트를 위한 유저 생성 함수 +const userGenerator = (username) => { + const testUser = new userModel({ + id: username, + name: username + "-name", + nickname: username + "-nickname", + profileImageUrl: generateProfileImageUrl(), + joinat: Date.now(), + subinfo: { + kaist: "20180668", + sparcs: "", + facebook: "", + twitter: "", + }, + email: username + ".kaist.ac.kr", + }); + + return testUser; +}; From d06256262ae050f3c25bd27d42cbd9562e667cc6 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 9 Nov 2022 03:20:56 +0900 Subject: [PATCH 007/308] Test: change describe statement --- test/auth.replace.js | 2 +- test/locations.js | 2 +- test/logininfo.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/auth.replace.js b/test/auth.replace.js index 732ae41c..7d39e0bd 100644 --- a/test/auth.replace.js +++ b/test/auth.replace.js @@ -4,7 +4,7 @@ const authHandlers = require("../src/service/auth.replace"); const { userModel } = require("../src/db/mongo"); const security = require("../security"); -describe("auth.replace handler", function () { +describe("[auth.replace] 1.sparcsssoHandler", function () { const removeTestUser = async () => { // drop all collections await userModel.deleteOne({ id: "test" }); diff --git a/test/locations.js b/test/locations.js index 022d749d..1c6d5397 100644 --- a/test/locations.js +++ b/test/locations.js @@ -1,7 +1,7 @@ const expect = require("chai").expect; const locationHandlers = require("../src/service/locations"); -describe("locations handler", function () { +describe("[locations] 1.getAllLocationsHandler", function () { it("should return information of locations correctly", async function () { const req = {}; const res = { diff --git a/test/logininfo.js b/test/logininfo.js index 2c82b0de..0b2a16a3 100644 --- a/test/logininfo.js +++ b/test/logininfo.js @@ -2,7 +2,7 @@ const expect = require("chai").expect; const logininfoHandlers = require("../src/service/logininfo"); const { userModel } = require("../src/db/mongo"); -describe("logininfo handler", function () { +describe("[logininfo] 1.logininfoHandler", function () { it("should return {id: undefined, sid: undefined, name: undefined } when no user is logged in", function () { const req = { session: {} }; const res = { @@ -64,7 +64,7 @@ describe("logininfo handler", function () { }); }); -describe("detail info handler", function () { +describe("[logininfo] 2.detailHandler", function () { it("should return { id: undefined } when no user is logged in", function () { const req = { session: {} }; const res = { From 9b4edf64e3b352d4a94cae818a1b58373c0385c7 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 9 Nov 2022 18:06:25 +0900 Subject: [PATCH 008/308] Add: utils.js --- test/rooms.js | 28 +++------------------------- test/utils.js | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 25 deletions(-) create mode 100644 test/utils.js diff --git a/test/rooms.js b/test/rooms.js index 2c89cfd0..fa120fe7 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -2,13 +2,12 @@ const expect = require("chai").expect; const express = require("express"); const roomsHandlers = require("../src/service/rooms"); const { userModel, roomModel, locationModel } = require("../src/db/mongo"); -const { generateProfileImageUrl } = require("../src/modules/modifyProfile"); +const { userGenerator } = require("./utils"); const app = express(); describe("[rooms] 1.createHandler", function () { it("should create room", async function () { - let testUser1 = userGenerator("test1"); - await testUser1.save(); + let testUser1 = await userGenerator("test1"); let testFrom = await locationModel.findOne({ koName: "대전역" }); let testTo = await locationModel.findOne({ koName: "택시승강장" }); const req = { @@ -59,8 +58,7 @@ describe("[rooms] 2.infoHandler", function () { describe("[rooms] 3.joinHandler", function () { it("should return information of room and join", async function () { - let testUser2 = userGenerator("test2"); - await testUser2.save(); + let testUser2 = await userGenerator("test2"); let testRoom = await roomModel.findOne({ name: "test-room" }); const req = { body: { @@ -204,23 +202,3 @@ describe("[rooms] 8.abortHandler", function () { after(removeTestData); }); }); - -// 테스트를 위한 유저 생성 함수 -const userGenerator = (username) => { - const testUser = new userModel({ - id: username, - name: username + "-name", - nickname: username + "-nickname", - profileImageUrl: generateProfileImageUrl(), - joinat: Date.now(), - subinfo: { - kaist: "20180668", - sparcs: "", - facebook: "", - twitter: "", - }, - email: username + ".kaist.ac.kr", - }); - - return testUser; -}; diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 00000000..006da2d4 --- /dev/null +++ b/test/utils.js @@ -0,0 +1,24 @@ +const { userModel } = require("../src/db/mongo"); +const { generateProfileImageUrl } = require("../src/modules/modifyProfile"); + +// 테스트를 위한 유저 생성 함수 +const userGenerator = async (username) => { + const testUser = new userModel({ + id: username, + name: username + "-name", + nickname: username + "-nickname", + profileImageUrl: generateProfileImageUrl(), + joinat: Date.now(), + subinfo: { + kaist: "20180668", + sparcs: "", + facebook: "", + twitter: "", + }, + email: username + ".kaist.ac.kr", + }); + await testUser.save(); + return testUser; +}; + +module.exports = { userGenerator }; From c569f7b7edd57e9c6db013cd8559a89b579d636a Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 9 Nov 2022 18:07:38 +0900 Subject: [PATCH 009/308] Refactor: replace let to const --- test/rooms.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index fa120fe7..d3492c8c 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -7,9 +7,9 @@ const app = express(); describe("[rooms] 1.createHandler", function () { it("should create room", async function () { - let testUser1 = await userGenerator("test1"); - let testFrom = await locationModel.findOne({ koName: "대전역" }); - let testTo = await locationModel.findOne({ koName: "택시승강장" }); + const testUser1 = await userGenerator("test1"); + const testFrom = await locationModel.findOne({ koName: "대전역" }); + const testTo = await locationModel.findOne({ koName: "택시승강장" }); const req = { body: { name: "test-room", @@ -36,8 +36,8 @@ describe("[rooms] 1.createHandler", function () { describe("[rooms] 2.infoHandler", function () { it("should return information of room", async function () { - let testUser1 = await userModel.findOne({ id: "test1" }); - let room = await roomModel.findOne({ name: "test-room" }); + const testUser1 = await userModel.findOne({ id: "test1" }); + const room = await roomModel.findOne({ name: "test-room" }); const req = { query: { id: room._id }, userId: testUser1.id, @@ -58,8 +58,8 @@ describe("[rooms] 2.infoHandler", function () { describe("[rooms] 3.joinHandler", function () { it("should return information of room and join", async function () { - let testUser2 = await userGenerator("test2"); - let testRoom = await roomModel.findOne({ name: "test-room" }); + const testUser2 = await userGenerator("test2"); + const testRoom = await roomModel.findOne({ name: "test-room" }); const req = { body: { roomId: testRoom._id, @@ -83,8 +83,8 @@ describe("[rooms] 3.joinHandler", function () { describe("[rooms] 4.searchHandler", function () { it("should return information of searching room", async function () { - let testFrom = await locationModel.findOne({ koName: "대전역" }); - let testTo = await locationModel.findOne({ koName: "택시승강장" }); + const testFrom = await locationModel.findOne({ koName: "대전역" }); + const testTo = await locationModel.findOne({ koName: "택시승강장" }); const req = { query: { name: "test-room", @@ -110,7 +110,7 @@ describe("[rooms] 4.searchHandler", function () { describe("[rooms] 5.searchByUserHandler", function () { it("should return information of searching room", async function () { - let testUser1 = await userModel.findOne({ id: "test1" }); + const testUser1 = await userModel.findOne({ id: "test1" }); const req = { userId: testUser1.id, }; @@ -130,8 +130,8 @@ describe("[rooms] 5.searchByUserHandler", function () { describe("[rooms] 6.commitPaymentHandler", function () { it("should return information of room and commit payment", async function () { - let testUser1 = await userModel.findOne({ id: "test1" }); - let testRoom = await roomModel.findOne({ name: "test-room" }); + const testUser1 = await userModel.findOne({ id: "test1" }); + const testRoom = await roomModel.findOne({ name: "test-room" }); const req = { body: { roomId: testRoom._id }, userId: testUser1.id, @@ -153,8 +153,8 @@ describe("[rooms] 6.commitPaymentHandler", function () { describe("[rooms] 7.settlementHandler", function () { it("should return information of room and set settlement", async function () { - let testUser2 = await userModel.findOne({ id: "test2" }); - let testRoom = await roomModel.findOne({ name: "test-room" }); + const testUser2 = await userModel.findOne({ id: "test2" }); + const testRoom = await roomModel.findOne({ name: "test-room" }); const req = { body: { roomId: testRoom._id }, userId: testUser2.id, @@ -181,8 +181,8 @@ describe("[rooms] 8.abortHandler", function () { await userModel.deleteOne({ id: "test2" }); }; it("should return information of room and abort user", async function () { - let testUser2 = await userModel.findOne({ id: "test2" }); - let testRoom = await roomModel.findOne({ name: "test-room" }); + const testUser2 = await userModel.findOne({ id: "test2" }); + const testRoom = await roomModel.findOne({ name: "test-room" }); const req = { body: { roomId: testRoom._id }, userId: testUser2.id, From 87a8985e14d84f21005c90c2d0bf0c2265595a82 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 9 Nov 2022 18:12:09 +0900 Subject: [PATCH 010/308] Test: fix to arrow function --- test/auth.replace.js | 4 ++-- test/locations.js | 4 ++-- test/logininfo.js | 16 ++++++++-------- test/rooms.js | 32 ++++++++++++++++---------------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/test/auth.replace.js b/test/auth.replace.js index 7d39e0bd..c8d7545f 100644 --- a/test/auth.replace.js +++ b/test/auth.replace.js @@ -4,7 +4,7 @@ const authHandlers = require("../src/service/auth.replace"); const { userModel } = require("../src/db/mongo"); const security = require("../security"); -describe("[auth.replace] 1.sparcsssoHandler", function () { +describe("[auth.replace] 1.sparcsssoHandler", () => { const removeTestUser = async () => { // drop all collections await userModel.deleteOne({ id: "test" }); @@ -12,7 +12,7 @@ describe("[auth.replace] 1.sparcsssoHandler", function () { before(removeTestUser); - it("should redirect to security.frontUrl after successful user creation", function () { + it("should redirect to security.frontUrl after successful user creation", () => { request(authHandlers.sparcsssoHandler) .post("/auth/sparcssso") .send({ diff --git a/test/locations.js b/test/locations.js index 1c6d5397..dcb3ec33 100644 --- a/test/locations.js +++ b/test/locations.js @@ -1,8 +1,8 @@ const expect = require("chai").expect; const locationHandlers = require("../src/service/locations"); -describe("[locations] 1.getAllLocationsHandler", function () { - it("should return information of locations correctly", async function () { +describe("[locations] 1.getAllLocationsHandler", () => { + it("should return information of locations correctly", async () => { const req = {}; const res = { json: (data) => { diff --git a/test/logininfo.js b/test/logininfo.js index 0b2a16a3..ad59cf61 100644 --- a/test/logininfo.js +++ b/test/logininfo.js @@ -2,8 +2,8 @@ const expect = require("chai").expect; const logininfoHandlers = require("../src/service/logininfo"); const { userModel } = require("../src/db/mongo"); -describe("[logininfo] 1.logininfoHandler", function () { - it("should return {id: undefined, sid: undefined, name: undefined } when no user is logged in", function () { +describe("[logininfo] 1.logininfoHandler", () => { + it("should return {id: undefined, sid: undefined, name: undefined } when no user is logged in", () => { const req = { session: {} }; const res = { json: (data) => { @@ -17,7 +17,7 @@ describe("[logininfo] 1.logininfoHandler", function () { logininfoHandlers.logininfoHandler(req, res); }); - it("should return {id: 'hello-id', sid: 'hello-sid', 'name': 'hello-name'} when user is logged in", function () { + it("should return {id: 'hello-id', sid: 'hello-sid', 'name': 'hello-name'} when user is logged in", () => { const req = { session: { loginInfo: { @@ -40,7 +40,7 @@ describe("[logininfo] 1.logininfoHandler", function () { logininfoHandlers.logininfoHandler(req, res); }); - it("should return {id: undefined, sid: undefined, name: undefined } when the session is expired", function () { + it("should return {id: undefined, sid: undefined, name: undefined } when the session is expired", () => { const req = { session: { loginInfo: { @@ -64,8 +64,8 @@ describe("[logininfo] 1.logininfoHandler", function () { }); }); -describe("[logininfo] 2.detailHandler", function () { - it("should return { id: undefined } when no user is logged in", function () { +describe("[logininfo] 2.detailHandler", () => { + it("should return { id: undefined } when no user is logged in", () => { const req = { session: {} }; const res = { json: (data) => { @@ -77,7 +77,7 @@ describe("[logininfo] 2.detailHandler", function () { logininfoHandlers.detailHandler(req, res); }); - it("should return correct information as same as user's when user is logged in", async function () { + it("should return correct information as same as user's when user is logged in", async () => { const req = { session: { loginInfo: { @@ -112,7 +112,7 @@ describe("[logininfo] 2.detailHandler", function () { logininfoHandlers.detailHandler(req, res); }); - it("should return {id: undefined} when the session is expired", function () { + it("should return {id: undefined} when the session is expired", () => { const req = { session: { loginInfo: { diff --git a/test/rooms.js b/test/rooms.js index d3492c8c..79556510 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -5,8 +5,8 @@ const { userModel, roomModel, locationModel } = require("../src/db/mongo"); const { userGenerator } = require("./utils"); const app = express(); -describe("[rooms] 1.createHandler", function () { - it("should create room", async function () { +describe("[rooms] 1.createHandler", () => { + it("should create room", async () => { const testUser1 = await userGenerator("test1"); const testFrom = await locationModel.findOne({ koName: "대전역" }); const testTo = await locationModel.findOne({ koName: "택시승강장" }); @@ -34,8 +34,8 @@ describe("[rooms] 1.createHandler", function () { }); }); -describe("[rooms] 2.infoHandler", function () { - it("should return information of room", async function () { +describe("[rooms] 2.infoHandler", () => { + it("should return information of room", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); const room = await roomModel.findOne({ name: "test-room" }); const req = { @@ -56,8 +56,8 @@ describe("[rooms] 2.infoHandler", function () { }); }); -describe("[rooms] 3.joinHandler", function () { - it("should return information of room and join", async function () { +describe("[rooms] 3.joinHandler", () => { + it("should return information of room and join", async () => { const testUser2 = await userGenerator("test2"); const testRoom = await roomModel.findOne({ name: "test-room" }); const req = { @@ -81,8 +81,8 @@ describe("[rooms] 3.joinHandler", function () { }); }); -describe("[rooms] 4.searchHandler", function () { - it("should return information of searching room", async function () { +describe("[rooms] 4.searchHandler", () => { + it("should return information of searching room", async () => { const testFrom = await locationModel.findOne({ koName: "대전역" }); const testTo = await locationModel.findOne({ koName: "택시승강장" }); const req = { @@ -108,8 +108,8 @@ describe("[rooms] 4.searchHandler", function () { }); }); -describe("[rooms] 5.searchByUserHandler", function () { - it("should return information of searching room", async function () { +describe("[rooms] 5.searchByUserHandler", () => { + it("should return information of searching room", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); const req = { userId: testUser1.id, @@ -128,8 +128,8 @@ describe("[rooms] 5.searchByUserHandler", function () { }); }); -describe("[rooms] 6.commitPaymentHandler", function () { - it("should return information of room and commit payment", async function () { +describe("[rooms] 6.commitPaymentHandler", () => { + it("should return information of room and commit payment", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); const testRoom = await roomModel.findOne({ name: "test-room" }); const req = { @@ -151,8 +151,8 @@ describe("[rooms] 6.commitPaymentHandler", function () { }); }); -describe("[rooms] 7.settlementHandler", function () { - it("should return information of room and set settlement", async function () { +describe("[rooms] 7.settlementHandler", () => { + it("should return information of room and set settlement", async () => { const testUser2 = await userModel.findOne({ id: "test2" }); const testRoom = await roomModel.findOne({ name: "test-room" }); const req = { @@ -173,14 +173,14 @@ describe("[rooms] 7.settlementHandler", function () { }); }); -describe("[rooms] 8.abortHandler", function () { +describe("[rooms] 8.abortHandler", () => { const removeTestData = async () => { // drop all testData await roomModel.deleteOne({ name: "test-room" }); await userModel.deleteOne({ id: "test1" }); await userModel.deleteOne({ id: "test2" }); }; - it("should return information of room and abort user", async function () { + it("should return information of room and abort user", async () => { const testUser2 = await userModel.findOne({ id: "test2" }); const testRoom = await roomModel.findOne({ name: "test-room" }); const req = { From 05d3c95c63b5639835b96f5306f18580a644a29a Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 15 Nov 2022 23:00:05 +0900 Subject: [PATCH 011/308] Test: fix rooms.js --- test/rooms.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index 79556510..42501274 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -15,7 +15,7 @@ describe("[rooms] 1.createHandler", () => { name: "test-room", from: testFrom._id, to: testTo._id, - time: Date.now(), + time: Date.now() + 60 * 60 * 1000, maxPartLength: 4, }, userId: testUser1.id, @@ -90,7 +90,8 @@ describe("[rooms] 4.searchHandler", () => { name: "test-room", from: testFrom._id, to: testTo._id, - time: Date.now() - 1000, + time: Date.now(), + withTime: true, maxPartLength: 4, }, }; @@ -135,7 +136,7 @@ describe("[rooms] 6.commitPaymentHandler", () => { const req = { body: { roomId: testRoom._id }, userId: testUser1.id, - timestamp: Date.now(), + timestamp: Date.now() + 60 * 60 * 1000, }; const res = { send: (data) => { From 6f2ff532052e6e856c6d27fef51d454e6cc50c55 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 15 Nov 2022 23:09:46 +0900 Subject: [PATCH 012/308] Test: fix create room tests --- test/rooms.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index 42501274..73b16ca0 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -6,7 +6,7 @@ const { userGenerator } = require("./utils"); const app = express(); describe("[rooms] 1.createHandler", () => { - it("should create room", async () => { + it("should create room which departs after 1 minute", async () => { const testUser1 = await userGenerator("test1"); const testFrom = await locationModel.findOne({ koName: "대전역" }); const testTo = await locationModel.findOne({ koName: "택시승강장" }); @@ -15,7 +15,7 @@ describe("[rooms] 1.createHandler", () => { name: "test-room", from: testFrom._id, to: testTo._id, - time: Date.now() + 60 * 60 * 1000, + time: Date.now() + 60 * 1000, maxPartLength: 4, }, userId: testUser1.id, @@ -136,7 +136,7 @@ describe("[rooms] 6.commitPaymentHandler", () => { const req = { body: { roomId: testRoom._id }, userId: testUser1.id, - timestamp: Date.now() + 60 * 60 * 1000, + timestamp: Date.now() + 60 * 1000, }; const res = { send: (data) => { From 28ea1684256dcfe8515fa04749b2a326950994c2 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 29 Nov 2022 01:08:38 +0900 Subject: [PATCH 013/308] Refactor: branch update --- test/rooms.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/rooms.js b/test/rooms.js index 73b16ca0..379389d6 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -5,6 +5,8 @@ const { userModel, roomModel, locationModel } = require("../src/db/mongo"); const { userGenerator } = require("./utils"); const app = express(); +// rooms.js 관련 8개의 handler을 테스트 +// 1. test1이 1분 뒤에 출발하는 test-room 방을 생성 describe("[rooms] 1.createHandler", () => { it("should create room which departs after 1 minute", async () => { const testUser1 = await userGenerator("test1"); @@ -34,6 +36,7 @@ describe("[rooms] 1.createHandler", () => { }); }); +// 2. test1을 통하여 방의 정보 가져옴 describe("[rooms] 2.infoHandler", () => { it("should return information of room", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); @@ -56,6 +59,7 @@ describe("[rooms] 2.infoHandler", () => { }); }); +// 3. test2가 test-room에 join describe("[rooms] 3.joinHandler", () => { it("should return information of room and join", async () => { const testUser2 = await userGenerator("test2"); @@ -81,6 +85,7 @@ describe("[rooms] 3.joinHandler", () => { }); }); +// 4. 방의 정보를 통해 검색 describe("[rooms] 4.searchHandler", () => { it("should return information of searching room", async () => { const testFrom = await locationModel.findOne({ koName: "대전역" }); @@ -109,6 +114,7 @@ describe("[rooms] 4.searchHandler", () => { }); }); +// 5. 방에 속한 유저를 통해 검색 describe("[rooms] 5.searchByUserHandler", () => { it("should return information of searching room", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); @@ -129,6 +135,7 @@ describe("[rooms] 5.searchByUserHandler", () => { }); }); +// 6.1분이 지난 후, 정산 정보를 불러옴 describe("[rooms] 6.commitPaymentHandler", () => { it("should return information of room and commit payment", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); @@ -152,6 +159,7 @@ describe("[rooms] 6.commitPaymentHandler", () => { }); }); +// 7. 도착 정보를 불러옴 describe("[rooms] 7.settlementHandler", () => { it("should return information of room and set settlement", async () => { const testUser2 = await userModel.findOne({ id: "test2" }); @@ -174,6 +182,7 @@ describe("[rooms] 7.settlementHandler", () => { }); }); +// 8. test2 방에서 퇴장, 생성해준 data 모두 삭제 describe("[rooms] 8.abortHandler", () => { const removeTestData = async () => { // drop all testData From 7c5d4e8b117066e8a29b165f90dce3978a05763a Mon Sep 17 00:00:00 2001 From: seowan Date: Tue, 29 Nov 2022 22:33:32 +0900 Subject: [PATCH 014/308] caanot create rooms over 2 weeks --- src/service/rooms.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/service/rooms.js b/src/service/rooms.js index f7140e99..c3411c3e 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -17,6 +17,24 @@ const createHandler = async (req, res) => { error: "Room/create : locations are same", }); } + + const createTime = new Date(time); + createTime.setHours(0, 0, 0, 0); + + const currentTime = new Date(); + currentTime.setHours(0, 0, 0, 0); + + const maxTime = new Date(currentTime); + maxTime.setDate(currentTime.getDate() + 15); + + maxTime.setHours(0, 0, 0, 0); + + if (createTime.getTime() > maxTime.getTime()) { + return res.status(400).json({ + error: "Room/create : cannot over 2 weeks on the basis of current Date", + }); + } + let fromLoc = await locationModel.findById(from); let toLoc = await locationModel.findById(to); if (!fromLoc || !toLoc) { @@ -303,7 +321,7 @@ const searchHandler = async (req, res) => { // 검색 시간대는 해당 날짜의 자정으로 설정합니다. const maxTime = new Date(minTime); - maxTime.setDate(minTime.getDate() + 14); + maxTime.setDate(minTime.getDate() + (time ? 1 : 14)); maxTime.setHours(0); maxTime.setMinutes(0); maxTime.setSeconds(0); From d2df4e03b56e51090eef597a1a7b83e07cd829cc Mon Sep 17 00:00:00 2001 From: seowan Date: Tue, 29 Nov 2022 22:34:24 +0900 Subject: [PATCH 015/308] fix number --- src/service/rooms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/rooms.js b/src/service/rooms.js index c3411c3e..ce8879a9 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -25,7 +25,7 @@ const createHandler = async (req, res) => { currentTime.setHours(0, 0, 0, 0); const maxTime = new Date(currentTime); - maxTime.setDate(currentTime.getDate() + 15); + maxTime.setDate(currentTime.getDate() + 14); maxTime.setHours(0, 0, 0, 0); From 12423e067aa4604a6dd624cef069c26a83b97a2a Mon Sep 17 00:00:00 2001 From: seowan Date: Tue, 29 Nov 2022 23:11:24 +0900 Subject: [PATCH 016/308] divide daterange home page and search page --- src/service/rooms.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/service/rooms.js b/src/service/rooms.js index ce8879a9..90d592ca 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -279,7 +279,7 @@ const abortHandler = async (req, res) => { const searchHandler = async (req, res) => { try { - const { name, from, to, time, withTime, maxPartLength } = req.query; + const { name, from, to, time, withTime, maxPartLength, isHome } = req.query; // 출발지와 도착지가 같은 경우 if (from && to && from === to) { @@ -321,7 +321,10 @@ const searchHandler = async (req, res) => { // 검색 시간대는 해당 날짜의 자정으로 설정합니다. const maxTime = new Date(minTime); - maxTime.setDate(minTime.getDate() + (time ? 1 : 14)); + + // home -> 7, search -> 14 + const timeRange = isHome ? 7 : 14; + maxTime.setDate(minTime.getDate() + (time ? 1 : timeRange)); maxTime.setHours(0); maxTime.setMinutes(0); maxTime.setSeconds(0); From 70880c1efc4cf374c8152c4557babf24ada989c7 Mon Sep 17 00:00:00 2001 From: seowan Date: Tue, 29 Nov 2022 23:14:02 +0900 Subject: [PATCH 017/308] validate isHome --- src/route/rooms.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/route/rooms.js b/src/route/rooms.js index ca3d3cf7..32f20a78 100644 --- a/src/route/rooms.js +++ b/src/route/rooms.js @@ -64,6 +64,7 @@ router.get( query("time").optional().isISO8601(), query("withTime").toBoolean().optional().isBoolean(), query("maxPartLength").optional().isInt({ min: 2, max: 4 }), + query("isHome").isBoolean(), ], validator, roomHandlers.searchHandler From 2fa92d26232ceeafa0f7095e81d220bdd82f1306 Mon Sep 17 00:00:00 2001 From: seowan Date: Tue, 29 Nov 2022 23:17:21 +0900 Subject: [PATCH 018/308] add validate toBoolean() --- src/route/rooms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/route/rooms.js b/src/route/rooms.js index 32f20a78..d43e4352 100644 --- a/src/route/rooms.js +++ b/src/route/rooms.js @@ -64,7 +64,7 @@ router.get( query("time").optional().isISO8601(), query("withTime").toBoolean().optional().isBoolean(), query("maxPartLength").optional().isInt({ min: 2, max: 4 }), - query("isHome").isBoolean(), + query("isHome").toBoolean().isBoolean(), ], validator, roomHandlers.searchHandler From d217bd73810ad16e8a8e80952f264b979ebc8726 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sat, 3 Dec 2022 23:17:02 +0900 Subject: [PATCH 019/308] Refactor: add testRemover and remove status checkings --- test/rooms.js | 41 +++++++++-------------------------------- test/utils.js | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index 379389d6..d1ec2265 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -2,14 +2,16 @@ const expect = require("chai").expect; const express = require("express"); const roomsHandlers = require("../src/service/rooms"); const { userModel, roomModel, locationModel } = require("../src/db/mongo"); -const { userGenerator } = require("./utils"); +const { userGenerator, testRemover } = require("./utils"); const app = express(); +let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; + // rooms.js 관련 8개의 handler을 테스트 // 1. test1이 1분 뒤에 출발하는 test-room 방을 생성 describe("[rooms] 1.createHandler", () => { it("should create room which departs after 1 minute", async () => { - const testUser1 = await userGenerator("test1"); + const testUser1 = await userGenerator("test1", testData); const testFrom = await locationModel.findOne({ koName: "대전역" }); const testTo = await locationModel.findOne({ koName: "택시승강장" }); const req = { @@ -27,9 +29,6 @@ describe("[rooms] 1.createHandler", () => { send: (data) => { expect(data).to.has.property("name", "test-room"); }, - status: (data) => { - expect(data).to.equal(200); - }, }; await roomsHandlers.createHandler(req, res); @@ -50,9 +49,6 @@ describe("[rooms] 2.infoHandler", () => { expect(data).to.has.property("name", "test-room"); expect(data).to.has.property("isOver"); }, - status: (data) => { - expect(data).to.equal(200); - }, }; await roomsHandlers.infoHandler(req, res); @@ -62,8 +58,9 @@ describe("[rooms] 2.infoHandler", () => { // 3. test2가 test-room에 join describe("[rooms] 3.joinHandler", () => { it("should return information of room and join", async () => { - const testUser2 = await userGenerator("test2"); + const testUser2 = await userGenerator("test2", testData); const testRoom = await roomModel.findOne({ name: "test-room" }); + testData["rooms"].push(testRoom); const req = { body: { roomId: testRoom._id, @@ -76,9 +73,6 @@ describe("[rooms] 3.joinHandler", () => { expect(data).to.has.property("name", "test-room"); expect(data.part).to.have.lengthOf(2); }, - status: (data) => { - expect(data).to.equal(200); - }, }; await roomsHandlers.joinHandler(req, res); @@ -105,9 +99,6 @@ describe("[rooms] 4.searchHandler", () => { expect(data[0]).to.has.property("name", "test-room"); expect(data[0]).to.has.property("settlementTotal"); }, - status: (data) => { - expect(data).to.equal(200); - }, }; await roomsHandlers.searchHandler(req, res); @@ -123,11 +114,8 @@ describe("[rooms] 5.searchByUserHandler", () => { }; const res = { json: (data) => { - expect(data).to.has.property("ongoing"); - expect(data).to.has.property("done"); - }, - status: (data) => { - expect(data).to.equal(200); + expect(data["ongoing"][0]).to.has.property("name", "test-room"); + expect(data["done"][0]).to.be.an("undefined"); }, }; @@ -151,9 +139,6 @@ describe("[rooms] 6.commitPaymentHandler", () => { expect(data).to.has.property("isOver", true); expect(data).to.has.property("settlementTotal", 1); }, - status: (data) => { - expect(data).to.equal(200); - }, }; await roomsHandlers.commitPaymentHandler(req, res); }); @@ -174,9 +159,6 @@ describe("[rooms] 7.settlementHandler", () => { expect(data).to.has.property("isOver", true); expect(data).to.has.property("settlementTotal", 2); }, - status: (data) => { - expect(data).to.equal(200); - }, }; await roomsHandlers.settlementHandler(req, res); }); @@ -186,9 +168,7 @@ describe("[rooms] 7.settlementHandler", () => { describe("[rooms] 8.abortHandler", () => { const removeTestData = async () => { // drop all testData - await roomModel.deleteOne({ name: "test-room" }); - await userModel.deleteOne({ id: "test1" }); - await userModel.deleteOne({ id: "test2" }); + await testRemover(testData); }; it("should return information of room and abort user", async () => { const testUser2 = await userModel.findOne({ id: "test2" }); @@ -204,9 +184,6 @@ describe("[rooms] 8.abortHandler", () => { expect(data).to.has.property("name", "test-room"); expect(data.part).to.have.lengthOf(1); }, - status: (data) => { - expect(data).to.equal(200); - }, }; await roomsHandlers.abortHandler(req, res); after(removeTestData); diff --git a/test/utils.js b/test/utils.js index 006da2d4..17aff5e5 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,8 +1,14 @@ -const { userModel } = require("../src/db/mongo"); +const { + userModel, + roomModel, + chatModel, + locationModel, + reportModel, +} = require("../src/db/mongo"); const { generateProfileImageUrl } = require("../src/modules/modifyProfile"); // 테스트를 위한 유저 생성 함수 -const userGenerator = async (username) => { +const userGenerator = async (username, testData) => { const testUser = new userModel({ id: username, name: username + "-name", @@ -18,7 +24,30 @@ const userGenerator = async (username) => { email: username + ".kaist.ac.kr", }); await testUser.save(); + testData["users"].push(testUser); return testUser; }; -module.exports = { userGenerator }; +const testRemover = async (testData) => { + for (const roomData of testData["rooms"]) { + await roomModel.deleteOne({ _id: roomData }); + } + + for (const userData of testData["users"]) { + await userModel.deleteOne({ _id: userData }); + } + + for (const chatData of testData["chat"]) { + await chatModel.deleteOne({ _id: chatData._id }); + } + + for (const locationData of testData["location"]) { + await locationModel.deleteOne({ _id: locationData._id }); + } + + for (const reportData of testData["report"]) { + await reportModel.deleteOne({ _id: reportData._id }); + } +}; + +module.exports = { userGenerator, testRemover }; From a31ac5dbd933e520484c269720b30882d67734f7 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sat, 3 Dec 2022 23:22:27 +0900 Subject: [PATCH 020/308] Test: add comment --- test/rooms.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/rooms.js b/test/rooms.js index d1ec2265..d97da381 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -106,6 +106,7 @@ describe("[rooms] 4.searchHandler", () => { }); // 5. 방에 속한 유저를 통해 검색 +// ongoing은 test-room이 검색되고, done은 아무것도 검색되지 않아야함 describe("[rooms] 5.searchByUserHandler", () => { it("should return information of searching room", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); From 8de30f7b22fa74cacedb335e4f7e8c32b4d0c6f5 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 4 Dec 2022 00:09:20 +0900 Subject: [PATCH 021/308] Refactor: add branch in gitmodules --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index 4bb0b642..f15db4d8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "sampleGenerator"] path = sampleGenerator url = https://github.com/sparcs-kaist/taxiSampleGenerator + branch = main From d0494b901e9d32df23aa9f8c164764bc2ee754b0 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 4 Dec 2022 00:14:09 +0900 Subject: [PATCH 022/308] Refactor: add submodule update --- .github/workflows/test_ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 2cbd76d2..2664bc44 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -26,6 +26,8 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'npm' + - name: Update to lateset submoudle + run: git submodule update --remote - name: Install sampleGenerator dependencies from package-lock.json run: cd sampleGenerator && npm ci && cd .. - name: Install taxi-back dependencies from package-lock.json From 99395333c6d20dbd25bfa4353848fdd8e84d6ea7 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 4 Dec 2022 00:16:49 +0900 Subject: [PATCH 023/308] Fix: fix typo --- .github/workflows/test_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 2664bc44..a28d1c0d 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -26,7 +26,7 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'npm' - - name: Update to lateset submoudle + - name: Update to latest submoudle run: git submodule update --remote - name: Install sampleGenerator dependencies from package-lock.json run: cd sampleGenerator && npm ci && cd .. From bc3a2c1893688fc573154834ab1a25bff5a48b30 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 4 Dec 2022 05:04:37 +0900 Subject: [PATCH 024/308] Test: add users.js tests --- src/service/users.js | 14 ++---- test/rooms.js | 2 +- test/users.js | 113 +++++++++++++++++++++++++++++++++++++++++++ test/utils.js | 4 ++ 4 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 test/users.js diff --git a/src/service/users.js b/src/service/users.js index 30c2df0e..a032a492 100644 --- a/src/service/users.js +++ b/src/service/users.js @@ -8,11 +8,9 @@ const agreeOnTermsOfServiceHandler = async (req, res) => { if (user.agreeOnTermsOfService !== true) { user.agreeOnTermsOfService = true; await user.save(); - res - .status(200) - .send( - "User/agreeOnTermsOfService : agree on Terms of Service successful" - ); + res.send( + "User/agreeOnTermsOfService : agree on Terms of Service successful" + ); } else { res.status(400).send("User/agreeOnTermsOfService : already agreed"); } @@ -27,7 +25,7 @@ const getAgreeOnTermsOfServiceHandler = async (req, res) => { .findOne({ id: req.userId }, "agreeOnTermsOfService") .lean(); const agreeOnTermsOfService = user.agreeOnTermsOfService === true; - res.status(200).json({ agreeOnTermsOfService }); + res.json({ agreeOnTermsOfService }); } catch { res.status(500).send("/getAgreeOnTermsOfService : internal server error"); } @@ -41,9 +39,7 @@ const editNicknameHandler = (req, res) => { .findOneAndUpdate({ id: req.userId }, { nickname: newNickname }) .then((result) => { if (result) { - res - .status(200) - .send("User/editNickname : edit user nickname successful"); + res.send("User/editNickname : edit user nickname successful"); } else { res.status(400).send("User/editNickname : such user id does not exist"); } diff --git a/test/rooms.js b/test/rooms.js index d97da381..84b4af49 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -187,6 +187,6 @@ describe("[rooms] 8.abortHandler", () => { }, }; await roomsHandlers.abortHandler(req, res); - after(removeTestData); + afterEach(removeTestData); }); }); diff --git a/test/users.js b/test/users.js new file mode 100644 index 00000000..2472e88b --- /dev/null +++ b/test/users.js @@ -0,0 +1,113 @@ +const expect = require("chai").expect; +const express = require("express"); +const usersHandlers = require("../src/service/users"); +const { userModel, roomModel, locationModel } = require("../src/db/mongo"); +const { userGenerator, testRemover } = require("./utils"); + +let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; + +// users.js 관련 5개의 handler을 테스트 +// 1. test1 유저를 생성 후, agreeOnTermsOfServiceHandler가 제대로 send 하는지 확인 +describe("[users] 1.agreeOnTermsOfServiceHandler", () => { + it("should return correct response from handler", async () => { + let testUser1 = await userGenerator("test1", testData); + const msg = + "User/agreeOnTermsOfService : agree on Terms of Service successful"; + const req = { + userId: testUser1.id, + }; + const res = { + send: (data) => { + expect(data).to.equal(msg); + }, + }; + + await usersHandlers.agreeOnTermsOfServiceHandler(req, res); + }); +}); + +// 2. test1 유저의 agreeOnTermsOfService 정보를 가져옴 +describe("[users] 2.getAgreeOnTermsOfServiceHandler", () => { + it("should return AgreeOnTermsOfService of user", async () => { + const testUser1 = await userModel.findOne({ id: "test1" }); + const req = { + userId: testUser1.id, + }; + const res = { + json: (data) => { + expect(data).to.has.property("agreeOnTermsOfService", true); + }, + }; + + await usersHandlers.getAgreeOnTermsOfServiceHandler(req, res); + }); +}); + +// 3. test1의 nickname을 test-nickname으로 변경 +describe("[users] 3.editNicknameHandler", () => { + it("should return correct response from handler", async () => { + const testUser1 = await userModel.findOne({ id: "test1" }); + const req = { + userId: testUser1.id, + body: { + nickname: "test-nickname", + }, + }; + const res = { + send: (data) => { + expect(data).to.equal( + "User/editNickname : edit user nickname successful" + ); + }, + }; + + usersHandlers.editNicknameHandler(req, res); + }); + + it("should be changed to new nickname", async () => { + const testUser1 = await userModel.findOne({ id: "test1" }); + expect(testUser1).to.have.property("nickname", "test-nickname"); + }); +}); + +// 4. 방의 정보를 통해 검색 +describe("[users] 4.editProfileImgGetPUrlHandler", () => { + it("should return url and fields of data", async () => { + const testUser1 = await userModel.findOne({ id: "test1" }); + const req = { + userId: testUser1.id, + body: { + type: "image/jpg", + }, + }; + const res = { + json: (data) => { + expect(data).to.have.property("url"); + expect(data.fields).to.have.property( + "key", + `profile-img/${testUser1._id}` + ); + }, + }; + + await usersHandlers.editProfileImgGetPUrlHandler(req, res); + }); +}); + +// 5. test1 user의 profileImageUrl 주소를 변경 +describe("[users] 5.editProfileImgDoneHandler", () => { + it("should return correct result and new profileImageUrl", async () => { + const testUser1 = await userModel.findOne({ id: "test1" }); + const req = { + userId: testUser1.id, + }; + const res = { + json: (data) => { + expect(data).to.have.property("result", true); + expect(data).to.have.property("profileImageUrl", testUser1._id); + }, + }; + + await usersHandlers.editProfileImgDoneHandler(req, res); + }); +}); diff --git a/test/utils.js b/test/utils.js index 17aff5e5..7c8fae45 100644 --- a/test/utils.js +++ b/test/utils.js @@ -22,6 +22,10 @@ const userGenerator = async (username, testData) => { twitter: "", }, email: username + ".kaist.ac.kr", + withdraw: false, + ban: false, + agreeOnTermsOfService: false, + isAdmin: false, }); await testUser.save(); testData["users"].push(testUser); From a57cb47c44f61fc1027d148d1759f2e091474f75 Mon Sep 17 00:00:00 2001 From: happycastle <41810556+happycastle114@users.noreply.github.com> Date: Wed, 4 Jan 2023 18:47:58 +0900 Subject: [PATCH 025/308] Add: FCM functions --- .env.example | 1 + app.js | 10 + package-lock.json | 2916 ++++++++++++++++++++++++++++++++++++++++++-- package.json | 1 + security.js | 1 + src/modules/fcm.js | 55 + 6 files changed, 2898 insertions(+), 86 deletions(-) create mode 100644 src/modules/fcm.js diff --git a/.env.example b/.env.example index 7a951fa4..54182910 100644 --- a/.env.example +++ b/.env.example @@ -10,3 +10,4 @@ AWS_SECRET_ACCESS_KEY=[AWS Secret access key] AWS_S3_BUCKET_NAME=[AWS S3 Buck name] JWT_SECRET_KEY=[JWT SERCRET KEY] APP_URI_SCHEME=[APP_URI_SCHEME] +GOOGLE_APPLICATION_CREDENTIALS=[GOOGLE_APPLICATION_CREDENTIALS_LOCATION] \ No newline at end of file diff --git a/app.js b/app.js index 26c5aff3..6b558719 100644 --- a/app.js +++ b/app.js @@ -11,6 +11,16 @@ const logger = require("./src/modules/logger"); const logAPIAccess = require("./src/modules/logAPIAccess"); const startSocketServer = require("./src/modules/socket"); +// Firebase Admin 초기설정 + +var admin = require("firebase-admin"); + +var serviceAccount = require(security.googleApplicationCredentials); + +admin.initializeApp({ + credential: admin.credential.cert(serviceAccount), +}); + // 익스프레스 서버 생성 const app = express(); app.use(express.urlencoded({ extended: false })); diff --git a/package-lock.json b/package-lock.json index 93a20f06..dc545d31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "express-session": "^1.17.1", "express-socket.io-session": "^1.3.5", "express-validator": "^6.14.0", + "firebase-admin": "^11.4.1", "jsonwebtoken": "^8.5.1", "mongoose": "^6.5.2", "querystring": "^0.2.1", @@ -2030,6 +2031,97 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@fastify/busboy": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.1.0.tgz", + "integrity": "sha512-Fv854f94v0CzIDllbY3i/0NJPNBRNLDawf3BTYVGCe9VrIIs3Wi7AFx24F9NzCxdf0wyx/x0Q9kEVnvDOPnlxA==", + "dependencies": { + "text-decoding": "^1.0.0" + }, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==", + "peer": true + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.7.tgz", + "integrity": "sha512-yA/dTveGGPcc85JP8ZE/KZqfGQyQTBCV10THdI8HTlP1GDvNrhr//J5jAt58MlsCOaO3XmC4DqScPBbtIsR/EA==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.21.tgz", + "integrity": "sha512-12MMQ/ulfygKpEJpseYMR0HunJdlsLrwx2XcEs40M18jocy2+spyzHHEwegN3x/2/BLFBjR5247Etmz0G97Qpg==", + "dependencies": { + "@firebase/util": "1.7.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.13.10.tgz", + "integrity": "sha512-KRucuzZ7ZHQsRdGEmhxId5jyM2yKsjsQWF9yv0dIhlxYg0D8rCVDZc/waoPKA5oV3/SEIoptF8F7R1Vfe7BCQA==", + "dependencies": { + "@firebase/auth-interop-types": "0.1.7", + "@firebase/component": "0.5.21", + "@firebase/logger": "0.3.4", + "@firebase/util": "1.7.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.2.10.tgz", + "integrity": "sha512-fK+IgUUqVKcWK/gltzDU+B1xauCOfY6vulO8lxoNTkcCGlSxuTtwsdqjGkFmgFRMYjXFWWJ6iFcJ/vXahzwCtA==", + "dependencies": { + "@firebase/component": "0.5.21", + "@firebase/database": "0.13.10", + "@firebase/database-types": "0.9.17", + "@firebase/logger": "0.3.4", + "@firebase/util": "1.7.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "0.9.17", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.17.tgz", + "integrity": "sha512-YQm2tCZyxNtEnlS5qo5gd2PAYgKCy69tUKwioGhApCFThW+mIgZs7IeYeJo2M51i4LCixYUl+CvnOyAnb/c3XA==", + "dependencies": { + "@firebase/app-types": "0.8.1", + "@firebase/util": "1.7.3" + } + }, + "node_modules/@firebase/database-types/node_modules/@firebase/app-types": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.8.1.tgz", + "integrity": "sha512-p75Ow3QhB82kpMzmOntv866wH9eZ3b4+QbUY+8/DA5Zzdf1c8Nsk8B7kbFpzJt4wwHMdy5LTF5YUnoTc1JiWkw==" + }, + "node_modules/@firebase/logger": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.4.tgz", + "integrity": "sha512-hlFglGRgZEwoyClZcGLx/Wd+zoLfGmbDkFx56mQt/jJ0XMbfPqwId1kiPl0zgdWZX+D8iH+gT6GuLPFsJWgiGw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/util": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.7.3.tgz", + "integrity": "sha512-wxNqWbqokF551WrJ9BIFouU/V5SL1oYCGx1oudcirdhadnQRFH5v1sjgGL7cUV/UsekSycygphdrF2lxBxOYKg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@floating-ui/core": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.1.tgz", @@ -2043,6 +2135,124 @@ "@floating-ui/core": "^1.0.1" } }, + "node_modules/@google-cloud/firestore": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-6.4.1.tgz", + "integrity": "sha512-5q4sl1XCL8NH2y82KZ4WQGHDOPnrSMYq3JpIeKD5C0OCSb4MfckOTB9LeAQ3p5tlL+7UsVRHj0SyzKz27XZJjw==", + "optional": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^3.5.1", + "protobufjs": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz", + "integrity": "sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", + "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz", + "integrity": "sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@google-cloud/storage": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.8.0.tgz", + "integrity": "sha512-eRGsHrhVA7NORehYW9jLUWHRzYqFxbYiG3LQL50ZhjMekDwzhPKUQ7wbjAji9OFcO3Mk8jeNHeWdpAc/QZANCg==", + "optional": true, + "dependencies": { + "@google-cloud/paginator": "^3.0.7", + "@google-cloud/projectify": "^3.0.0", + "@google-cloud/promisify": "^3.0.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "compressible": "^2.0.12", + "duplexify": "^4.0.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "gaxios": "^5.0.0", + "google-auth-library": "^8.0.1", + "mime": "^3.0.0", + "mime-types": "^2.0.8", + "p-limit": "^3.0.1", + "retry-request": "^5.0.0", + "teeny-request": "^8.0.0", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@google-cloud/storage/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.3.tgz", + "integrity": "sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==", + "optional": true, + "dependencies": { + "@grpc/proto-loader": "^0.7.0", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.4.tgz", + "integrity": "sha512-MnWjkGwqQ3W8fx94/c1CwqLsNmHHv2t0CFn+9++6+cDphC1lolpg9M2OU0iebIjK//pBNX9e94ho+gjx6vz39w==", + "optional": true, + "dependencies": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^7.0.0", + "yargs": "^16.2.0" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@hello-pangea/dnd": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.0.1.tgz", @@ -2197,6 +2407,14 @@ "node": ">= 8" } }, + "node_modules/@panva/asn1.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", + "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@popperjs/core": { "version": "2.11.6", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", @@ -2206,6 +2424,70 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "optional": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "optional": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "optional": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "optional": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "optional": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "optional": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "optional": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "optional": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "optional": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "optional": true + }, "node_modules/@redis/bloom": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.0.2.tgz", @@ -2976,6 +3258,15 @@ "url": "https://github.com/sponsors/ueberdosis" } }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "optional": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/@types/babel-core": { "version": "6.25.7", "resolved": "https://registry.npmjs.org/@types/babel-core/-/babel-core-6.25.7.tgz", @@ -3026,11 +3317,28 @@ "@types/babel-types": "*" } }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, "node_modules/@types/component-emitter": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -3046,6 +3354,27 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" }, + "node_modules/@types/express": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", + "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.31", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.32", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz", + "integrity": "sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -3055,6 +3384,47 @@ "hoist-non-react-statics": "^3.3.0" } }, + "node_modules/@types/jsonwebtoken": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", + "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "optional": true + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "optional": true + }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "optional": true, + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "optional": true + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, "node_modules/@types/node": { "version": "18.7.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.5.tgz", @@ -3070,6 +3440,16 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, "node_modules/@types/react": { "version": "18.0.24", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.24.tgz", @@ -3101,6 +3481,15 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -3132,6 +3521,18 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "optional": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3213,6 +3614,18 @@ "admin": "cli.js" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3287,6 +3700,15 @@ "node": ">=8" } }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -3318,6 +3740,15 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3489,6 +3920,15 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "optional": true, + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -3531,6 +3971,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "optional": true + }, "node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -3753,6 +4199,18 @@ } ] }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "optional": true, + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/chai": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", @@ -3863,7 +4321,7 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "devOptional": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -3968,6 +4426,18 @@ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "optional": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4404,6 +4874,18 @@ "node": ">=12" } }, + "node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "optional": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -4426,7 +4908,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "devOptional": true }, "node_modules/enabled": { "version": "2.0.0", @@ -4441,6 +4923,15 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/engine.io": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", @@ -4469,6 +4960,21 @@ "node": ">=10.0.0" } }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "optional": true + }, + "node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "optional": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4560,18 +5066,100 @@ "node": ">=0.8.0" } }, - "node_modules/eslint": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", - "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "optional": true, "dependencies": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.10.4", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "optional": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "optional": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "optional": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "optional": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "optional": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", + "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", + "dependencies": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.1", @@ -4792,6 +5380,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "optional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", @@ -4843,6 +5444,15 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -5072,6 +5682,12 @@ } ] }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "optional": true + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5119,6 +5735,12 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "dev": true }, + "node_modules/fast-text-encoding": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", + "optional": true + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -5127,6 +5749,17 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", @@ -5225,6 +5858,84 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase-admin": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-11.4.1.tgz", + "integrity": "sha512-t5+Pf8rC01TW1KPD5U8Q45AEn7eK+FJaHlpzYStFb62J+MQmN/kB/PWUEsNn+7MNAQ0DZxFUCgJoi+bRmf83oQ==", + "dependencies": { + "@fastify/busboy": "^1.1.0", + "@firebase/database-compat": "^0.2.6", + "@firebase/database-types": "^0.9.13", + "@types/node": ">=12.12.47", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^2.1.4", + "node-forge": "^1.3.1", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^6.4.0", + "@google-cloud/storage": "^6.5.2" + } + }, + "node_modules/firebase-admin/node_modules/jsonwebtoken": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "dependencies": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/firebase-admin/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/firebase-admin/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/firebase-admin/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/firebase-admin/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/flat": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", @@ -5376,6 +6087,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gaxios": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.0.2.tgz", + "integrity": "sha512-TjtV2AJOZoMQqRYoy5eM8cCQogYwazWNYLQ72QB0kwa6vHHruYkGmhhyrlzbmgNHK1dNnuP2WSH81urfzyN2Og==", + "optional": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.7" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gcp-metadata": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz", + "integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==", + "optional": true, + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/generic-pool": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", @@ -5396,7 +6135,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, + "devOptional": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -5495,11 +6234,104 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-auth-library": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.7.0.tgz", + "integrity": "sha512-1M0NG5VDIvJZEnstHbRdckLZESoJwguinwN8Dhae0j2ZKIQFIV63zxm6Fo6nM4xkgqUr2bbMtV5Dgo+Hy6oo0Q==", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.0.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/google-gax": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-3.5.2.tgz", + "integrity": "sha512-AyP53w0gHcWlzxm+jSgqCR3Xu4Ld7EpSjhtNBnNhzwwWaIUyphH9kBGNIEH+i4UGkTUXOY29K/Re8EiAvkBRGw==", + "optional": true, + "dependencies": { + "@grpc/grpc-js": "~1.7.0", + "@grpc/proto-loader": "^0.7.0", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^8.0.2", + "is-stream-ended": "^0.1.4", + "node-fetch": "^2.6.1", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^1.0.0", + "protobufjs": "7.1.2", + "protobufjs-cli": "1.0.2", + "retry-request": "^5.0.0" + }, + "bin": { + "compileProtos": "build/tools/compileProtos.js", + "minifyProtoJson": "build/tools/minify.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/google-gax/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "optional": true, + "dependencies": { + "node-forge": "^1.3.1" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "optional": true + }, "node_modules/grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" }, + "node_modules/gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "optional": true, + "dependencies": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/gud": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", @@ -5622,6 +6454,38 @@ "node": ">= 0.8" } }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/i18next": { "version": "21.10.0", "resolved": "https://registry.npmjs.org/i18next/-/i18next-21.10.0.tgz", @@ -5879,7 +6743,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -6020,6 +6884,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-stream-ended": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", + "optional": true + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -6146,6 +7016,20 @@ "node": ">= 0.6.0" } }, + "node_modules/jose": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.6.tgz", + "integrity": "sha512-FVoPY7SflDodE4lknJmbAHSUjLCzE2H1F6MS0RYKMQ8SR+lNccpMf8R4eqkNYyyUjR5qZReOzZo5C5YiHOCjjg==", + "dependencies": { + "@panva/asn1.js": "^1.0.0" + }, + "engines": { + "node": ">=10.13.0 < 13 || >=13.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6162,6 +7046,53 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "optional": true, + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "optional": true, + "dependencies": { + "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -6173,6 +7104,15 @@ "node": ">=4" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "optional": true, + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -6252,6 +7192,43 @@ "resolved": "https://registry.npmjs.org/jw-paginate/-/jw-paginate-1.0.4.tgz", "integrity": "sha512-W0bv782exgCoynUL/egbRpaYwf/r6T6e02H870H5u3hfSgEYrxgz5POwmFF5aApS6iPi6yhZ0VF8IbafNFsntA==" }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "optional": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.1.5.tgz", + "integrity": "sha512-IODtn1SwEm7n6GQZnQLY0oxKDrMh7n/jRH1MzE8mlxWMrh2NnMyOsXTebu8vJ1qCpmuTJcL4DdiE0E4h8jnwsA==", + "dependencies": { + "@types/express": "^4.17.14", + "@types/jsonwebtoken": "^8.5.9", + "debug": "^4.3.4", + "jose": "^2.0.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.1.4" + }, + "engines": { + "node": ">=10 < 13 || >=14" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "optional": true, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.4.1.tgz", @@ -6265,6 +7242,15 @@ "node": ">=0.10.0" } }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, "node_modules/kruptein": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/kruptein/-/kruptein-3.0.5.tgz", @@ -6293,11 +7279,25 @@ "node": ">= 0.8.0" } }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "optional": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/linkifyjs": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-3.0.5.tgz", @@ -6322,6 +7322,17 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "optional": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -6473,6 +7484,12 @@ "triple-beam": "^1.3.0" } }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "optional": true + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -6497,7 +7514,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -6505,6 +7521,29 @@ "node": ">=10" } }, + "node_modules/lru-memoizer": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz", + "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "dependencies": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -6533,6 +7572,50 @@ "semver": "bin/semver" } }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "optional": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.6", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.6.tgz", + "integrity": "sha512-jRW30YGywD2ESXDc+l17AiritL0uVaSnWsb26f+68qaW9zgbIIr1f4v2Nsvc0+s0Z2N3uX6t/yAw7BwCQ1wMsA==", + "optional": true, + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/marked": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.5.tgz", + "integrity": "sha512-jPueVhumq7idETHkb203WDD4fMA3yV9emQ5vLwop58lu8bTclMghBWcYAavlDqIEMaisADinV1TooIFCfqOsYQ==", + "optional": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "optional": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -6644,6 +7727,27 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mocha": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", @@ -6889,6 +7993,56 @@ "node": ">= 0.6" } }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "optional": true + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "optional": true + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "optional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -7481,52 +8635,247 @@ "w3c-keyname": "^2.2.0" } }, - "node_modules/prosemirror-model": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz", - "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==", + "node_modules/prosemirror-model": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz", + "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.2.tgz", + "integrity": "sha512-rd0pqSDp86p0MUMKG903g3I9VmElFkQpkZ2iOd3EOVg1vo5Cst51rAsoE+5IPy0LPXq64eGcCYlW1+JPNxOj2w==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.2.tgz", + "integrity": "sha512-puuzLD2mz/oTdfgd8msFbe0A42j5eNudKAAPDB0+QJRw8cO1ygjLmhLrg9RvDpf87Dkd6D4t93qdef00KKNacQ==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.0.tgz", + "integrity": "sha512-O4T697Cqilw06Zvc3Wm+e237R6eZtJL/xGMliCi+Uo8VL6qHk6afz1qq0zNjT3eZMuYwnP8ZS0+YxX/tfcE9TQ==", + "dependencies": { + "prosemirror-model": "^1.0.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.29.0.tgz", + "integrity": "sha512-bifVd5aD9uCNtpLL1AyhquG/cVbNZSv+ALBxTEGYv51a6OHDhq+aOuzqq4MermNdeBdT+5uyURXCALgzk0EN5g==", + "dependencies": { + "prosemirror-model": "^1.16.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "node_modules/proto3-json-serializer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.0.tgz", + "integrity": "sha512-SjXwUWe/vANGs/mJJTbw5++7U67nwsymg7qsoPtw6GiXqw3kUy8ByojrlEdVE2efxAdKreX8WkDafxvYW95ZQg==", + "optional": true, + "dependencies": { + "protobufjs": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.2.tgz", + "integrity": "sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs-cli": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.0.2.tgz", + "integrity": "sha512-cz9Pq9p/Zs7okc6avH20W7QuyjTclwJPgqXG11jNaulfS3nbVisID8rC+prfgq0gbZE0w9LBFd1OKFF03kgFzg==", + "optional": true, + "dependencies": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^3.6.3", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "protobufjs": "^7.0.0" + } + }, + "node_modules/protobufjs-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/protobufjs-cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/protobufjs-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "optional": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/protobufjs-cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/protobufjs-cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "optional": true + }, + "node_modules/protobufjs-cli/node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "optional": true, "dependencies": { - "orderedmap": "^2.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/prosemirror-schema-list": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.2.tgz", - "integrity": "sha512-rd0pqSDp86p0MUMKG903g3I9VmElFkQpkZ2iOd3EOVg1vo5Cst51rAsoE+5IPy0LPXq64eGcCYlW1+JPNxOj2w==", - "dependencies": { - "prosemirror-model": "^1.0.0", - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.0.0" + "node_modules/protobufjs-cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "optional": true, + "engines": { + "node": ">=8" } }, - "node_modules/prosemirror-state": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.2.tgz", - "integrity": "sha512-puuzLD2mz/oTdfgd8msFbe0A42j5eNudKAAPDB0+QJRw8cO1ygjLmhLrg9RvDpf87Dkd6D4t93qdef00KKNacQ==", + "node_modules/protobufjs-cli/node_modules/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "optional": true, "dependencies": { - "prosemirror-model": "^1.0.0", - "prosemirror-transform": "^1.0.0", - "prosemirror-view": "^1.27.0" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, - "node_modules/prosemirror-transform": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.0.tgz", - "integrity": "sha512-O4T697Cqilw06Zvc3Wm+e237R6eZtJL/xGMliCi+Uo8VL6qHk6afz1qq0zNjT3eZMuYwnP8ZS0+YxX/tfcE9TQ==", + "node_modules/protobufjs-cli/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "optional": true, "dependencies": { - "prosemirror-model": "^1.0.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/prosemirror-view": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.29.0.tgz", - "integrity": "sha512-bifVd5aD9uCNtpLL1AyhquG/cVbNZSv+ALBxTEGYv51a6OHDhq+aOuzqq4MermNdeBdT+5uyURXCALgzk0EN5g==", + "node_modules/protobufjs-cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "optional": true, "dependencies": { - "prosemirror-model": "^1.16.0", - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.1.0" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, + "node_modules/protobufjs/node_modules/long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==", + "optional": true + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -7539,6 +8888,11 @@ "node": ">= 0.10" } }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -8039,11 +9393,20 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "optional": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -8100,6 +9463,28 @@ "node": ">=8" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", + "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "optional": true, + "dependencies": { + "debug": "^4.1.1", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -8504,6 +9889,21 @@ "node": ">= 0.8" } }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "optional": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "optional": true + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -8535,7 +9935,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "devOptional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -8593,6 +9993,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "optional": true + }, "node_modules/styled-components": { "version": "5.3.6", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", @@ -8764,6 +10170,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", + "optional": true + }, + "node_modules/teeny-request": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.2.tgz", + "integrity": "sha512-34pe0a4zASseXZCKdeTiIZqSKA8ETHb1EwItZr01PAR3CLPojeAKgSjzeNS4373gi59hNulyDrPKEbh2zO9sCg==", + "optional": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/terser": { "version": "5.15.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", @@ -8786,6 +10223,11 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/text-decoding": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz", + "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==" + }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -8814,6 +10256,18 @@ "@popperjs/core": "^2.9.0" } }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "optional": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -8872,8 +10326,7 @@ "node_modules/tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "peer": true + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/type-check": { "version": "0.4.0", @@ -8918,6 +10371,24 @@ "node": ">= 0.6" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "optional": true + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -8949,6 +10420,12 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "optional": true + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -9170,6 +10647,27 @@ "node": ">=12" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/whatwg-url": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", @@ -9298,7 +10796,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, + "devOptional": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -9315,7 +10813,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -9330,7 +10828,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -9342,7 +10840,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "devOptional": true }, "node_modules/wrappy": { "version": "1.0.2", @@ -9386,6 +10884,12 @@ "node": ">=4.0" } }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "optional": true + }, "node_modules/xss": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz", @@ -9410,7 +10914,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=10" } @@ -9432,7 +10936,7 @@ "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, + "devOptional": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -9450,7 +10954,7 @@ "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=10" } @@ -10864,6 +12368,93 @@ } } }, + "@fastify/busboy": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.1.0.tgz", + "integrity": "sha512-Fv854f94v0CzIDllbY3i/0NJPNBRNLDawf3BTYVGCe9VrIIs3Wi7AFx24F9NzCxdf0wyx/x0Q9kEVnvDOPnlxA==", + "requires": { + "text-decoding": "^1.0.0" + } + }, + "@firebase/app-types": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==", + "peer": true + }, + "@firebase/auth-interop-types": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.7.tgz", + "integrity": "sha512-yA/dTveGGPcc85JP8ZE/KZqfGQyQTBCV10THdI8HTlP1GDvNrhr//J5jAt58MlsCOaO3XmC4DqScPBbtIsR/EA==", + "requires": {} + }, + "@firebase/component": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.21.tgz", + "integrity": "sha512-12MMQ/ulfygKpEJpseYMR0HunJdlsLrwx2XcEs40M18jocy2+spyzHHEwegN3x/2/BLFBjR5247Etmz0G97Qpg==", + "requires": { + "@firebase/util": "1.7.3", + "tslib": "^2.1.0" + } + }, + "@firebase/database": { + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.13.10.tgz", + "integrity": "sha512-KRucuzZ7ZHQsRdGEmhxId5jyM2yKsjsQWF9yv0dIhlxYg0D8rCVDZc/waoPKA5oV3/SEIoptF8F7R1Vfe7BCQA==", + "requires": { + "@firebase/auth-interop-types": "0.1.7", + "@firebase/component": "0.5.21", + "@firebase/logger": "0.3.4", + "@firebase/util": "1.7.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "@firebase/database-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.2.10.tgz", + "integrity": "sha512-fK+IgUUqVKcWK/gltzDU+B1xauCOfY6vulO8lxoNTkcCGlSxuTtwsdqjGkFmgFRMYjXFWWJ6iFcJ/vXahzwCtA==", + "requires": { + "@firebase/component": "0.5.21", + "@firebase/database": "0.13.10", + "@firebase/database-types": "0.9.17", + "@firebase/logger": "0.3.4", + "@firebase/util": "1.7.3", + "tslib": "^2.1.0" + } + }, + "@firebase/database-types": { + "version": "0.9.17", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.17.tgz", + "integrity": "sha512-YQm2tCZyxNtEnlS5qo5gd2PAYgKCy69tUKwioGhApCFThW+mIgZs7IeYeJo2M51i4LCixYUl+CvnOyAnb/c3XA==", + "requires": { + "@firebase/app-types": "0.8.1", + "@firebase/util": "1.7.3" + }, + "dependencies": { + "@firebase/app-types": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.8.1.tgz", + "integrity": "sha512-p75Ow3QhB82kpMzmOntv866wH9eZ3b4+QbUY+8/DA5Zzdf1c8Nsk8B7kbFpzJt4wwHMdy5LTF5YUnoTc1JiWkw==" + } + } + }, + "@firebase/logger": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.4.tgz", + "integrity": "sha512-hlFglGRgZEwoyClZcGLx/Wd+zoLfGmbDkFx56mQt/jJ0XMbfPqwId1kiPl0zgdWZX+D8iH+gT6GuLPFsJWgiGw==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@firebase/util": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.7.3.tgz", + "integrity": "sha512-wxNqWbqokF551WrJ9BIFouU/V5SL1oYCGx1oudcirdhadnQRFH5v1sjgGL7cUV/UsekSycygphdrF2lxBxOYKg==", + "requires": { + "tslib": "^2.1.0" + } + }, "@floating-ui/core": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.1.tgz", @@ -10877,6 +12468,96 @@ "@floating-ui/core": "^1.0.1" } }, + "@google-cloud/firestore": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-6.4.1.tgz", + "integrity": "sha512-5q4sl1XCL8NH2y82KZ4WQGHDOPnrSMYq3JpIeKD5C0OCSb4MfckOTB9LeAQ3p5tlL+7UsVRHj0SyzKz27XZJjw==", + "optional": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^3.5.1", + "protobufjs": "^7.0.0" + } + }, + "@google-cloud/paginator": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz", + "integrity": "sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==", + "optional": true, + "requires": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + } + }, + "@google-cloud/projectify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", + "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", + "optional": true + }, + "@google-cloud/promisify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz", + "integrity": "sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==", + "optional": true + }, + "@google-cloud/storage": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.8.0.tgz", + "integrity": "sha512-eRGsHrhVA7NORehYW9jLUWHRzYqFxbYiG3LQL50ZhjMekDwzhPKUQ7wbjAji9OFcO3Mk8jeNHeWdpAc/QZANCg==", + "optional": true, + "requires": { + "@google-cloud/paginator": "^3.0.7", + "@google-cloud/projectify": "^3.0.0", + "@google-cloud/promisify": "^3.0.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "compressible": "^2.0.12", + "duplexify": "^4.0.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "gaxios": "^5.0.0", + "google-auth-library": "^8.0.1", + "mime": "^3.0.0", + "mime-types": "^2.0.8", + "p-limit": "^3.0.1", + "retry-request": "^5.0.0", + "teeny-request": "^8.0.0", + "uuid": "^8.0.0" + }, + "dependencies": { + "mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "optional": true + } + } + }, + "@grpc/grpc-js": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.3.tgz", + "integrity": "sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==", + "optional": true, + "requires": { + "@grpc/proto-loader": "^0.7.0", + "@types/node": ">=12.12.47" + } + }, + "@grpc/proto-loader": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.4.tgz", + "integrity": "sha512-MnWjkGwqQ3W8fx94/c1CwqLsNmHHv2t0CFn+9++6+cDphC1lolpg9M2OU0iebIjK//pBNX9e94ho+gjx6vz39w==", + "optional": true, + "requires": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^7.0.0", + "yargs": "^16.2.0" + } + }, "@hello-pangea/dnd": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.0.1.tgz", @@ -10993,14 +12674,83 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@panva/asn1.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", + "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==" + }, + "@popperjs/core": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "optional": true + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "optional": true + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "optional": true + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "optional": true + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "optional": true, + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" } }, - "@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "optional": true + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "optional": true + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "optional": true + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "optional": true + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "optional": true }, "@redis/bloom": { "version": "1.0.2", @@ -11528,6 +13278,12 @@ "@tiptap/extension-text": "^2.0.0-beta.17" } }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "optional": true + }, "@types/babel-core": { "version": "6.25.7", "resolved": "https://registry.npmjs.org/@types/babel-core/-/babel-core-6.25.7.tgz", @@ -11578,11 +13334,28 @@ "@types/babel-types": "*" } }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, "@types/component-emitter": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, "@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -11598,6 +13371,27 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" }, + "@types/express": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", + "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.31", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.32", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz", + "integrity": "sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -11607,6 +13401,47 @@ "hoist-non-react-statics": "^3.3.0" } }, + "@types/jsonwebtoken": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", + "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", + "requires": { + "@types/node": "*" + } + }, + "@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "optional": true + }, + "@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "optional": true + }, + "@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "optional": true, + "requires": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "optional": true + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, "@types/node": { "version": "18.7.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.5.tgz", @@ -11622,6 +13457,16 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, "@types/react": { "version": "18.0.24", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.24.tgz", @@ -11653,6 +13498,15 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, "@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -11684,6 +13538,15 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "optional": true, + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -11751,6 +13614,15 @@ "xss": "^1.0.13" } }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "requires": { + "debug": "4" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -11806,6 +13678,12 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "optional": true + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -11834,6 +13712,15 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, + "async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "optional": true, + "requires": { + "retry": "0.13.1" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -11958,6 +13845,12 @@ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, + "bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "optional": true + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -11985,6 +13878,12 @@ } } }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "optional": true + }, "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -12137,6 +14036,15 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001377.tgz", "integrity": "sha512-I5XeHI1x/mRSGl96LFOaSk528LA/yZG3m3iQgImGujjO8gotd/DL8QaI1R1h1dg5ATeI2jqPblMpKq4Tr5iKfQ==" }, + "catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "optional": true, + "requires": { + "lodash": "^4.17.15" + } + }, "chai": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", @@ -12217,7 +14125,7 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "devOptional": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -12307,6 +14215,15 @@ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "optional": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -12612,6 +14529,18 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==" }, + "duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "optional": true, + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -12634,7 +14563,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "devOptional": true }, "enabled": { "version": "2.0.0", @@ -12646,6 +14575,15 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "optional": true, + "requires": { + "once": "^1.4.0" + } + }, "engine.io": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", @@ -12668,6 +14606,18 @@ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "optional": true + }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "optional": true + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -12743,6 +14693,66 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "optional": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "optional": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "optional": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "optional": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "optional": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, "eslint": { "version": "8.22.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", @@ -12902,6 +14912,12 @@ "eslint-visitor-keys": "^3.3.0" } }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "optional": true + }, "esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", @@ -12938,6 +14954,12 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "optional": true + }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -13113,6 +15135,12 @@ "validator": "^13.7.0" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "optional": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -13156,6 +15184,12 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "dev": true }, + "fast-text-encoding": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", + "optional": true + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -13164,6 +15198,14 @@ "reusify": "^1.0.4" } }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, "fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", @@ -13246,6 +15288,68 @@ "path-exists": "^4.0.0" } }, + "firebase-admin": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-11.4.1.tgz", + "integrity": "sha512-t5+Pf8rC01TW1KPD5U8Q45AEn7eK+FJaHlpzYStFb62J+MQmN/kB/PWUEsNn+7MNAQ0DZxFUCgJoi+bRmf83oQ==", + "requires": { + "@fastify/busboy": "^1.1.0", + "@firebase/database-compat": "^0.2.6", + "@firebase/database-types": "^0.9.13", + "@google-cloud/firestore": "^6.4.0", + "@google-cloud/storage": "^6.5.2", + "@types/node": ">=12.12.47", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^2.1.4", + "node-forge": "^1.3.1", + "uuid": "^9.0.0" + }, + "dependencies": { + "jsonwebtoken": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "requires": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + } + } + }, "flat": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", @@ -13348,6 +15452,28 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, + "gaxios": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.0.2.tgz", + "integrity": "sha512-TjtV2AJOZoMQqRYoy5eM8cCQogYwazWNYLQ72QB0kwa6vHHruYkGmhhyrlzbmgNHK1dNnuP2WSH81urfzyN2Og==", + "optional": true, + "requires": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.7" + } + }, + "gcp-metadata": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz", + "integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==", + "optional": true, + "requires": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + } + }, "generic-pool": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", @@ -13362,7 +15488,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "devOptional": true }, "get-func-name": { "version": "2.0.0", @@ -13428,11 +15554,84 @@ "slash": "^3.0.0" } }, + "google-auth-library": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.7.0.tgz", + "integrity": "sha512-1M0NG5VDIvJZEnstHbRdckLZESoJwguinwN8Dhae0j2ZKIQFIV63zxm6Fo6nM4xkgqUr2bbMtV5Dgo+Hy6oo0Q==", + "optional": true, + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.0.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-gax": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-3.5.2.tgz", + "integrity": "sha512-AyP53w0gHcWlzxm+jSgqCR3Xu4Ld7EpSjhtNBnNhzwwWaIUyphH9kBGNIEH+i4UGkTUXOY29K/Re8EiAvkBRGw==", + "optional": true, + "requires": { + "@grpc/grpc-js": "~1.7.0", + "@grpc/proto-loader": "^0.7.0", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^8.0.2", + "is-stream-ended": "^0.1.4", + "node-fetch": "^2.6.1", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^1.0.0", + "protobufjs": "7.1.2", + "protobufjs-cli": "1.0.2", + "retry-request": "^5.0.0" + }, + "dependencies": { + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "optional": true + } + } + }, + "google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "optional": true, + "requires": { + "node-forge": "^1.3.1" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "optional": true + }, "grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" }, + "gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "optional": true, + "requires": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + } + }, "gud": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", @@ -13524,6 +15723,32 @@ "toidentifier": "1.0.1" } }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "optional": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "optional": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, "i18next": { "version": "21.10.0", "resolved": "https://registry.npmjs.org/i18next/-/i18next-21.10.0.tgz", @@ -13687,7 +15912,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "devOptional": true }, "is-generator-function": { "version": "1.0.10", @@ -13777,6 +16002,12 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, + "is-stream-ended": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", + "optional": true + }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -13863,17 +16094,65 @@ "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" }, + "jose": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.6.tgz", + "integrity": "sha512-FVoPY7SflDodE4lknJmbAHSUjLCzE2H1F6MS0RYKMQ8SR+lNccpMf8R4eqkNYyyUjR5qZReOzZo5C5YiHOCjjg==", + "requires": { + "@panva/asn1.js": "^1.0.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "optional": true, + "requires": { + "xmlcreate": "^2.0.4" + } + }, + "jsdoc": { + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "optional": true, "requires": { - "argparse": "^2.0.1" + "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.13.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "optional": true + } } }, "jsesc": { @@ -13881,6 +16160,15 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "optional": true, + "requires": { + "bignumber.js": "^9.0.0" + } + }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -13949,6 +16237,40 @@ "resolved": "https://registry.npmjs.org/jw-paginate/-/jw-paginate-1.0.4.tgz", "integrity": "sha512-W0bv782exgCoynUL/egbRpaYwf/r6T6e02H870H5u3hfSgEYrxgz5POwmFF5aApS6iPi6yhZ0VF8IbafNFsntA==" }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "optional": true, + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jwks-rsa": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.1.5.tgz", + "integrity": "sha512-IODtn1SwEm7n6GQZnQLY0oxKDrMh7n/jRH1MzE8mlxWMrh2NnMyOsXTebu8vJ1qCpmuTJcL4DdiE0E4h8jnwsA==", + "requires": { + "@types/express": "^4.17.14", + "@types/jsonwebtoken": "^8.5.9", + "debug": "^4.3.4", + "jose": "^2.0.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.1.4" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "optional": true, + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "kareem": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.4.1.tgz", @@ -13959,6 +16281,15 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, + "klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, "kruptein": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/kruptein/-/kruptein-3.0.5.tgz", @@ -13981,11 +16312,25 @@ "type-check": "~0.4.0" } }, + "limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "optional": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, "linkifyjs": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-3.0.5.tgz", @@ -14004,6 +16349,17 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "optional": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -14130,6 +16486,12 @@ "triple-beam": "^1.3.0" } }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "optional": true + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -14151,11 +16513,35 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } }, + "lru-memoizer": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz", + "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==", + "requires": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "requires": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + } + } + }, "magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -14180,6 +16566,38 @@ } } }, + "markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "optional": true, + "requires": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "markdown-it-anchor": { + "version": "8.6.6", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.6.tgz", + "integrity": "sha512-jRW30YGywD2ESXDc+l17AiritL0uVaSnWsb26f+68qaW9zgbIIr1f4v2Nsvc0+s0Z2N3uX6t/yAw7BwCQ1wMsA==", + "optional": true, + "requires": {} + }, + "marked": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.5.tgz", + "integrity": "sha512-jPueVhumq7idETHkb203WDD4fMA3yV9emQ5vLwop58lu8bTclMghBWcYAavlDqIEMaisADinV1TooIFCfqOsYQ==", + "optional": true + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "optional": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -14261,6 +16679,18 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "optional": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true + }, "mocha": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", @@ -14446,6 +16876,44 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "optional": true, + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "optional": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "optional": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "optional": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -14928,6 +17396,152 @@ "prosemirror-transform": "^1.1.0" } }, + "proto3-json-serializer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.0.tgz", + "integrity": "sha512-SjXwUWe/vANGs/mJJTbw5++7U67nwsymg7qsoPtw6GiXqw3kUy8ByojrlEdVE2efxAdKreX8WkDafxvYW95ZQg==", + "optional": true, + "requires": { + "protobufjs": "^7.0.0" + } + }, + "protobufjs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.2.tgz", + "integrity": "sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ==", + "optional": true, + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "dependencies": { + "long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==", + "optional": true + } + } + }, + "protobufjs-cli": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.0.2.tgz", + "integrity": "sha512-cz9Pq9p/Zs7okc6avH20W7QuyjTclwJPgqXG11jNaulfS3nbVisID8rC+prfgq0gbZE0w9LBFd1OKFF03kgFzg==", + "optional": true, + "requires": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^3.6.3", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "optional": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "optional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "optional": true + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "optional": true + }, + "minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "optional": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -14937,6 +17551,11 @@ "ipaddr.js": "1.9.1" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, "pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -15279,7 +17898,16 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "devOptional": true + }, + "requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "optional": true, + "requires": { + "lodash": "^4.17.21" + } }, "resolve": { "version": "1.22.1", @@ -15321,6 +17949,22 @@ "signal-exit": "^3.0.2" } }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "optional": true + }, + "retry-request": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", + "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "optional": true, + "requires": { + "debug": "^4.1.1", + "extend": "^3.0.2" + } + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -15641,6 +18285,21 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "optional": true, + "requires": { + "stubs": "^3.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "optional": true + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -15660,7 +18319,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "devOptional": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -15700,6 +18359,12 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "optional": true + }, "styled-components": { "version": "5.3.6", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", @@ -15828,6 +18493,33 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", + "optional": true + }, + "teeny-request": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.2.tgz", + "integrity": "sha512-34pe0a4zASseXZCKdeTiIZqSKA8ETHb1EwItZr01PAR3CLPojeAKgSjzeNS4373gi59hNulyDrPKEbh2zO9sCg==", + "optional": true, + "requires": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "dependencies": { + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "optional": true + } + } + }, "terser": { "version": "5.15.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", @@ -15846,6 +18538,11 @@ } } }, + "text-decoding": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz", + "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==" + }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -15874,6 +18571,15 @@ "@popperjs/core": "^2.9.0" } }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "optional": true, + "requires": { + "rimraf": "^3.0.0" + } + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -15917,8 +18623,7 @@ "tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "peer": true + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "type-check": { "version": "0.4.0", @@ -15948,6 +18653,18 @@ "mime-types": "~2.1.24" } }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "optional": true + }, + "uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true + }, "uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -15973,6 +18690,12 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "optional": true + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -16132,6 +18855,21 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, "whatwg-url": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", @@ -16227,7 +18965,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, + "devOptional": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -16238,7 +18976,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "requires": { "color-convert": "^2.0.1" } @@ -16247,7 +18985,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "requires": { "color-name": "~1.1.4" } @@ -16256,7 +18994,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "devOptional": true } } }, @@ -16285,6 +19023,12 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==" }, + "xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "optional": true + }, "xss": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz", @@ -16305,7 +19049,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "devOptional": true }, "yallist": { "version": "4.0.0", @@ -16321,7 +19065,7 @@ "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, + "devOptional": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -16336,7 +19080,7 @@ "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true + "devOptional": true }, "yargs-unparser": { "version": "2.0.0", diff --git a/package.json b/package.json index f6ecb4c8..7597219c 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "express-session": "^1.17.1", "express-socket.io-session": "^1.3.5", "express-validator": "^6.14.0", + "firebase-admin": "^11.4.1", "jsonwebtoken": "^8.5.1", "mongoose": "^6.5.2", "querystring": "^0.2.1", diff --git a/security.js b/security.js index 0511ffe6..d8d40b57 100644 --- a/security.js +++ b/security.js @@ -18,4 +18,5 @@ module.exports = { }, jwtSecretKey: process.env.JWT_SECRET_KEY, appUriScheme: process.env.APP_URI_SCHEME, + googleApplicationCredentials: process.env.GOOGLE_APPLICATION_CREDENTIALS, }; diff --git a/src/modules/fcm.js b/src/modules/fcm.js new file mode 100644 index 00000000..7abd722a --- /dev/null +++ b/src/modules/fcm.js @@ -0,0 +1,55 @@ +var admin = require("firebase-admin"); +const logger = require("../modules/logger"); + +const sendNotificationMultipleUsers = (message, tokens) => { + const tokenMessage = { + ...message, + tokens: tokens, + }; + return admin + .messaging() + .sendMulticast(tokenMessage) + .then((response) => { + return response.failureCount; + }); +}; + +const sendNotification = (message, token) => { + const tokenMessage = { + ...message, + token: token, + }; + return admin + .messaging() + .send(message) + .then((response) => { + return true; + }) + .catch((error) => { + logger.error(error); + return false; + }); +}; + +const sendMessageTopic = (message, topic) => { + const topicMessage = { + ...message, + topic: topic, + }; + return admin + .messaging() + .send(message) + .then((response) => { + return true; + }) + .catch((error) => { + logger.error(error); + return false; + }); +}; + +module.exports = { + sendNotificationMultipleUsers: sendNotificationMultipleUsers, + sendNotification: sendNotification, + sendMessageTopic: sendMessageTopic, +}; From 25b0ce79bc571709ea2b7c717e7e38cefbe29cbc Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sat, 7 Jan 2023 23:14:19 +0900 Subject: [PATCH 026/308] Refactor: check submoudle version in git action --- .github/workflows/test_ci.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index a28d1c0d..18bbc362 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -26,8 +26,18 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'npm' - - name: Update to latest submoudle - run: git submodule update --remote + - id: submodule-local + run: echo "::set-output name=ver::`git log --pretty="%h" -1`" + - id: submodule-origin + run: echo "::set-output name=ver::`git log origin --pretty="%h" -1`" + - name: Print submodule version + run: echo ${{ steps.submodule-local.outputs.ver }} + - name: Check submodule version + if: ${{ steps.submodule-local.outputs.ver }} != ${{ steps.submodule-origin.outputs.ver }} + uses: actions/github-script@v3 + with: + script: | + core.setFailed('Please update submodule to the latest version') - name: Install sampleGenerator dependencies from package-lock.json run: cd sampleGenerator && npm ci && cd .. - name: Install taxi-back dependencies from package-lock.json From 58d3090ebfc5ab18a6c9df28feccef65dd1d25a6 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sat, 7 Jan 2023 23:37:49 +0900 Subject: [PATCH 027/308] Fix: change on of test_ci workflow --- .github/workflows/test_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 18bbc362..40eae6e9 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -5,7 +5,7 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main" ] + branches: [ "main", "dev" ] jobs: build: runs-on: ubuntu-latest From 04786b27f6a3ecba3bfe04b8cd5f735f01345f93 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sat, 7 Jan 2023 23:45:08 +0900 Subject: [PATCH 028/308] Refactor: change deprecated statement --- .github/workflows/test_ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 40eae6e9..23f86bc2 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -27,9 +27,11 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'npm' - id: submodule-local - run: echo "::set-output name=ver::`git log --pretty="%h" -1`" + name: Save local version of submodule + run: echo "{ver}::{`cd sampleGenerator && git log --pretty="%h" -1 && cd ..`}" >> $GITHHUB_OUTPUT - id: submodule-origin - run: echo "::set-output name=ver::`git log origin --pretty="%h" -1`" + name: Save origin version of submodule + run: echo "{ver}::{`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`}" >> $GITHHUB_OUTPUT - name: Print submodule version run: echo ${{ steps.submodule-local.outputs.ver }} - name: Check submodule version From 47c311b68b2fb26bd0fd9b48eaa1e9ab1594d0d1 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sat, 7 Jan 2023 23:46:54 +0900 Subject: [PATCH 029/308] Fix: fix typo --- .github/workflows/test_ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 23f86bc2..550eeb07 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -28,10 +28,10 @@ jobs: cache: 'npm' - id: submodule-local name: Save local version of submodule - run: echo "{ver}::{`cd sampleGenerator && git log --pretty="%h" -1 && cd ..`}" >> $GITHHUB_OUTPUT + run: echo "{ver}::{`cd sampleGenerator && git log --pretty="%h" -1 && cd ..`}" >> $GITHUB_OUTPUT - id: submodule-origin name: Save origin version of submodule - run: echo "{ver}::{`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`}" >> $GITHHUB_OUTPUT + run: echo "{ver}::{`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`}" >> $GITHUB_OUTPUT - name: Print submodule version run: echo ${{ steps.submodule-local.outputs.ver }} - name: Check submodule version From fc1de08fc991a06228742a29dd974158fd1415ec Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sat, 7 Jan 2023 23:49:36 +0900 Subject: [PATCH 030/308] Fix: fix typo --- .github/workflows/test_ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 550eeb07..9325324e 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -28,10 +28,10 @@ jobs: cache: 'npm' - id: submodule-local name: Save local version of submodule - run: echo "{ver}::{`cd sampleGenerator && git log --pretty="%h" -1 && cd ..`}" >> $GITHUB_OUTPUT + run: echo "{ver}={`cd sampleGenerator && git log --pretty="%h" -1 && cd ..`}" >> $GITHUB_OUTPUT - id: submodule-origin name: Save origin version of submodule - run: echo "{ver}::{`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`}" >> $GITHUB_OUTPUT + run: echo "{ver}={`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`}" >> $GITHUB_OUTPUT - name: Print submodule version run: echo ${{ steps.submodule-local.outputs.ver }} - name: Check submodule version From 60981aa13e6323d3d128a92a4cc6265b938e4f75 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sat, 7 Jan 2023 23:55:30 +0900 Subject: [PATCH 031/308] Fix: fix error --- .github/workflows/test_ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 9325324e..d6925a43 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -28,10 +28,10 @@ jobs: cache: 'npm' - id: submodule-local name: Save local version of submodule - run: echo "{ver}={`cd sampleGenerator && git log --pretty="%h" -1 && cd ..`}" >> $GITHUB_OUTPUT + run: echo "ver=`cd sampleGenerator && git log --pretty="%h" -1 && cd ..`" >> $GITHUB_OUTPUT - id: submodule-origin name: Save origin version of submodule - run: echo "{ver}={`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`}" >> $GITHUB_OUTPUT + run: echo "ver=`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`" >> $GITHUB_OUTPUT - name: Print submodule version run: echo ${{ steps.submodule-local.outputs.ver }} - name: Check submodule version From 4814bba5fd5b26808ddbafcd454088370dc000c0 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 8 Jan 2023 00:06:16 +0900 Subject: [PATCH 032/308] Fix: Remove print --- .github/workflows/test_ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index d6925a43..e84c0db4 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -32,8 +32,6 @@ jobs: - id: submodule-origin name: Save origin version of submodule run: echo "ver=`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`" >> $GITHUB_OUTPUT - - name: Print submodule version - run: echo ${{ steps.submodule-local.outputs.ver }} - name: Check submodule version if: ${{ steps.submodule-local.outputs.ver }} != ${{ steps.submodule-origin.outputs.ver }} uses: actions/github-script@v3 From 506f3355b3e8395ec2c7da44537819202b6cc900 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 8 Jan 2023 00:07:45 +0900 Subject: [PATCH 033/308] Refactor: update submodule --- sampleGenerator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sampleGenerator b/sampleGenerator index 374f7b14..a8bdc640 160000 --- a/sampleGenerator +++ b/sampleGenerator @@ -1 +1 @@ -Subproject commit 374f7b14c462e117aa2ed2ae3a718abae39ae527 +Subproject commit a8bdc640346de4bd26594a36bd4a442c04abdf2b From 6e82e055aa2468f6a1b08ce54b7d86b59f7a956d Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 8 Jan 2023 00:11:33 +0900 Subject: [PATCH 034/308] Fix: change if condition --- .github/workflows/test_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index e84c0db4..06194508 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -33,7 +33,7 @@ jobs: name: Save origin version of submodule run: echo "ver=`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`" >> $GITHUB_OUTPUT - name: Check submodule version - if: ${{ steps.submodule-local.outputs.ver }} != ${{ steps.submodule-origin.outputs.ver }} + if: ${{ steps.submodule-local.outputs.ver != steps.submodule-origin.outputs.ver }} uses: actions/github-script@v3 with: script: | From b4da7c9af70d389c8c7c72c23a2d80a309a51392 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 8 Jan 2023 00:17:23 +0900 Subject: [PATCH 035/308] Refactor: update error statement --- .github/workflows/test_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 06194508..6adf0ddf 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -37,7 +37,7 @@ jobs: uses: actions/github-script@v3 with: script: | - core.setFailed('Please update submodule to the latest version') + core.setFailed('Please update submodule to the latest version by using \"git submodule update --remote\"') - name: Install sampleGenerator dependencies from package-lock.json run: cd sampleGenerator && npm ci && cd .. - name: Install taxi-back dependencies from package-lock.json From 00f73563a24c8e3303fd927c741e6a9e61d2e14a Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 11 Jan 2023 23:12:24 +0900 Subject: [PATCH 036/308] Refactor: reorder auth handlers --- src/service/auth.js | 72 +++++++++++++++++++------------------- src/service/auth.mobile.js | 1 + 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/service/auth.js b/src/service/auth.js index 4ebfb4b3..9a48ef6c 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -83,42 +83,6 @@ const loginDone = (req, res, userData) => { ); }; -const loginFalse = (req, res) => { - res.redirect(security.frontUrl + "/login/false"); // 리엑트로 연결되나? -}; - -const generateTokenHandler = (req, res) => { - req.session.isApp = true; - sparcsssoHandler(req, res); -}; - -const sparcsssoHandler = (req, res) => { - const userInfo = getLoginInfo(req); - const { url, state } = client.getLoginParams(); - req.session.state = state; - res.redirect(url); -}; - -const sparcsssoCallbackHandler = (req, res) => { - const state1 = req.session.state; - const state2 = req.body.state || req.query.state; - - if (state1 !== state2) loginFalse(req, res); - else { - const code = req.body.code || req.query.code; - client.getUserInfo(code).then((userDataBefore) => { - const userData = transUserData(userDataBefore); - if (userData.isEligible || security.nodeEnv !== "production") { - if (req.session.isApp) { - createNewTokenHandler(req, res, userData); - } else { - loginDone(req, res, userData); - } - } else loginFalse(req, res); - }); - } -}; - const createNewTokenHandler = (req, res, userData) => { userModel.findOne( { id: userData.id }, @@ -152,11 +116,47 @@ const createNewTokenHandler = (req, res, userData) => { ); }; +const loginFalse = (req, res) => { + res.redirect(security.frontUrl + "/login/false"); // 리엑트로 연결되나? +}; + +const sparcsssoHandler = (req, res) => { + const userInfo = getLoginInfo(req); + const { url, state } = client.getLoginParams(); + req.session.state = state; + res.redirect(url); +}; + +const sparcsssoCallbackHandler = (req, res) => { + const state1 = req.session.state; + const state2 = req.body.state || req.query.state; + + if (state1 !== state2) loginFalse(req, res); + else { + const code = req.body.code || req.query.code; + client.getUserInfo(code).then((userDataBefore) => { + const userData = transUserData(userDataBefore); + if (userData.isEligible || security.nodeEnv !== "production") { + if (req.session.isApp) { + createNewTokenHandler(req, res, userData); + } else { + loginDone(req, res, userData); + } + } else loginFalse(req, res); + }); + } +}; + const logoutHandler = (req, res) => { logout(req, res); res.status(200).send("logged out successfully"); }; +const generateTokenHandler = (req, res) => { + req.session.isApp = true; + sparcsssoHandler(req, res); +}; + module.exports = { sparcsssoHandler, sparcsssoCallbackHandler, diff --git a/src/service/auth.mobile.js b/src/service/auth.mobile.js index d1c8aaa4..38f26dfd 100644 --- a/src/service/auth.mobile.js +++ b/src/service/auth.mobile.js @@ -3,6 +3,7 @@ const { deviceTokenModel } = require("../db/mongo"); const { login } = require("../auth/login"); const jwt = require("../modules/jwt"); +const logger = require("../modules/logger"); const { TOKEN_EXPIRED, TOKEN_INVALID } = require("../config/constants"); From f52b0bcc93658de4be773124ab511f83581cca57 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 11 Jan 2023 23:22:35 +0900 Subject: [PATCH 037/308] Add: account in userSchema --- sampleGenerator | 2 +- src/db/mongo.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sampleGenerator b/sampleGenerator index 374f7b14..a8bdc640 160000 --- a/sampleGenerator +++ b/sampleGenerator @@ -1 +1 @@ -Subproject commit 374f7b14c462e117aa2ed2ae3a718abae39ae527 +Subproject commit a8bdc640346de4bd26594a36bd4a442c04abdf2b diff --git a/src/db/mongo.js b/src/db/mongo.js index fffc6c89..90ab6084 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -23,6 +23,7 @@ const userSchema = Schema({ }, email: { type: String, required: true }, isAdmin: { type: Boolean, default: false }, //관리자 여부 + account: { type: String, default: "" }, //계좌번호 정보 }); const participantSchema = Schema({ From f7846b783f2bb8280054da5bec6a6d1950fb6217 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 11 Jan 2023 23:25:27 +0900 Subject: [PATCH 038/308] Refactor: logininfo.js --- src/service/logininfo.js | 49 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/service/logininfo.js b/src/service/logininfo.js index 7cb20323..4c705732 100644 --- a/src/service/logininfo.js +++ b/src/service/logininfo.js @@ -8,32 +8,31 @@ const logininfoHandler = (req, res) => { const detailHandler = (req, res) => { const user = getLoginInfo(req); - - if (user.id) { - userModel.findOne( - { id: user.id }, - "_id name nickname id withdraw ban joinat agreeOnTermsOfService subinfo email profileImageUrl", - (err, result) => { - if (err) res.json({ err: true }); - else if (!result) res.json({ err: true }); - else { - res.json({ - oid: result._id, - id: result.id, - name: result.name, - nickname: result.nickname, - withdraw: result.withdraw, - ban: result.ban, - joinat: result.joinat, - agreeOnTermsOfService: result.agreeOnTermsOfService, - subinfo: result.subinfo, - email: result.email, - profileImgUrl: result.profileImageUrl, - }); - } + if (!user.id) return res.json({ id: undefined }); + userModel.findOne( + { id: user.id }, + "_id name nickname id withdraw ban joinat agreeOnTermsOfService subinfo email profileImageUrl", + (err, result) => { + if (err) res.json({ err: true }); + else if (!result) res.json({ err: true }); + else { + res.json({ + oid: result._id, + id: result.id, + name: result.name, + nickname: result.nickname, + withdraw: result.withdraw, + ban: result.ban, + joinat: result.joinat, + agreeOnTermsOfService: result.agreeOnTermsOfService, + subinfo: result.subinfo, + email: result.email, + profileImgUrl: result.profileImageUrl, + account: result.account, + }); } - ); - } else res.json({ id: undefined }); + } + ); }; module.exports = { From 1efc5808ebd08e9e20fa43b84f05566c1d108a79 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 11 Jan 2023 23:34:00 +0900 Subject: [PATCH 039/308] Add: editAccountHandler in users.js --- src/route/users.js | 8 ++++++++ src/service/users.js | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/route/users.js b/src/route/users.js index 582abc0a..06f519de 100755 --- a/src/route/users.js +++ b/src/route/users.js @@ -31,6 +31,14 @@ router.post( userHandlers.editNicknameHandler ); +// 새 계좌번호를 받아 로그인된 유저의 계좌번호를 변경합니다. +router.post( + "/editAccount", + body("account").matches(patterns.user.account), + validator, + userHandlers.editAccountHandler +); + // 프로필 이미지를 업로드할 수 있는 Presigned-url을 발급합니다. router.post( "/editProfileImg/getPUrl", diff --git a/src/service/users.js b/src/service/users.js index 30c2df0e..b2666828 100644 --- a/src/service/users.js +++ b/src/service/users.js @@ -54,6 +54,25 @@ const editNicknameHandler = (req, res) => { }); }; +const editAccountHandler = async (req, res) => { + const newAccount = req.body.account; + + // 계좌번호를 갱신하고 결과를 반환 + userModel + .findOneAndUpdate({ id: req.userId }, { account: newAccount }) + .then((result) => { + if (result) { + res.status(200).send("User/editAccount : edit user account successful"); + } else { + res.status(400).send("User/editAccount : such user id does not exist"); + } + }) + .catch((err) => { + logger.error(err); + res.status(500).send("User/editAccount : internal server error"); + }); +}; + const editProfileImgGetPUrlHandler = async (req, res) => { try { const type = req.body.type; @@ -122,6 +141,7 @@ module.exports = { agreeOnTermsOfServiceHandler, getAgreeOnTermsOfServiceHandler, editNicknameHandler, + editAccountHandler, editProfileImgGetPUrlHandler, editProfileImgDoneHandler, }; From 3c35c2dcc32a5e095566d65f1958616497942903 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 11 Jan 2023 23:50:56 +0900 Subject: [PATCH 040/308] Add: account RE in pattern.js --- src/db/patterns.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/db/patterns.js b/src/db/patterns.js index 692a8b93..3c85ccef 100644 --- a/src/db/patterns.js +++ b/src/db/patterns.js @@ -8,6 +8,7 @@ module.exports = { nickname: RegExp("^[A-Za-z가-힣ㄱ-ㅎㅏ-ㅣ0-9-_ ]{3,25}$"), allowedEmployeeTypes: RegExp("^([PEUR]|[SA]|[PEUR][SA])$"), profileImgType: RegExp("^(image/png|image/jpg|image/jpeg)$"), + account: RegExp("^[A-Za-z가-힣]{2,7} [0-9]{10,14}$"), }, chat: { chatImgType: RegExp("^(image/png|image/jpg|image/jpeg)$"), From a6f33a1c6e4ca13cd78402bca56696aef2d32c6d Mon Sep 17 00:00:00 2001 From: withsang Date: Sat, 14 Jan 2023 16:23:15 +0900 Subject: [PATCH 041/308] Refactor: make deviecTokenModel camelCase --- src/db/mongo.js | 7 ++++++- src/service/auth.mobile.js | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/db/mongo.js b/src/db/mongo.js index fffc6c89..82780438 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -36,13 +36,18 @@ const participantSchema = Schema({ }); const deviceTokenSchema = Schema({ - userid: { + userId: { type: Schema.Types.ObjectId, ref: "User", required: true, unique: true, }, deviceToken: [{ type: String, required: true }], + registeredAt: { + type: Date, + default: () => Date.now(), + required: true, + }, }); const roomSchema = Schema({ diff --git a/src/service/auth.mobile.js b/src/service/auth.mobile.js index 38f26dfd..0d6f020f 100644 --- a/src/service/auth.mobile.js +++ b/src/service/auth.mobile.js @@ -95,10 +95,10 @@ const registerDeviceTokenHandler = async (req, res) => { await deviceTokenModel.updateOne( { - userid: accessTokenStatus.id, + userId: accessTokenStatus.id, }, { - userid: accessTokenStatus.id, + userId: accessTokenStatus.id, $addToSet: { deviceToken: deviceToken }, }, { upsert: true, new: true } @@ -126,9 +126,9 @@ const removeDeviceTokenHandler = async (req, res) => { await deviceTokenModel.updateOne( { - userid: accessTokenStatus.id, + userId: accessTokenStatus.id, }, - { userid: accessTokenStatus.id, $pull: { deviceToken: deviceToken } }, + { userId: accessTokenStatus.id, $pull: { deviceToken: deviceToken } }, { upsert: true, new: true } ); res.status(200).send("success"); From f85f0e8611027ab9b1156b26d5269db9d81ed154 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Sat, 14 Jan 2023 18:52:25 +0900 Subject: [PATCH 042/308] Add: adminIpWhitelistModel --- src/db/mongo.js | 5 +++++ src/middleware/adminAuth.js | 18 ++++++++++-------- src/route/admin.js | 3 ++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/db/mongo.js b/src/db/mongo.js index fffc6c89..1cdcb833 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -93,6 +93,10 @@ const reportSchema = Schema({ time: { type: Date, required: true }, }); +const adminIpWhitelistSchema = Schema({ + ip: { type: String, required: true }, +}); + const database = mongoose.connection; database.on("error", console.error.bind(console, "mongoose connection error.")); database.on("open", () => { @@ -125,4 +129,5 @@ module.exports = { locationModel: mongoose.model("Location", locationSchema), chatModel: mongoose.model("Chat", chatSchema), reportModel: mongoose.model("Report", reportSchema), + adminIpWhitelistModel: mongoose.model("AdminIpWhitelist", adminIpWhitelistSchema), }; diff --git a/src/middleware/adminAuth.js b/src/middleware/adminAuth.js index 2f57d47b..5caa203e 100644 --- a/src/middleware/adminAuth.js +++ b/src/middleware/adminAuth.js @@ -2,22 +2,24 @@ const { isLogin, getLoginInfo } = require("../auth/login"); const { frontUrl } = require("../../security"); -const { userModel } = require("../db/mongo"); +const { userModel, adminIpWhitelistModel } = require("../db/mongo"); const adminAuthMiddleware = async (req, res, next) => { // Check if user is logged in if (!isLogin(req)) { - res.redirect(frontUrl); - return; + return res.redirect(frontUrl); } try { + // 관리자 유무를 확인 const { id } = getLoginInfo(req); const user = await userModel.findOne({ id }); - if (user.isAdmin) { - next(); - } else { - res.redirect(frontUrl); - } + if (!user.isAdmin) res.redirect(frontUrl); + + // 접속한 IP가 화이트리스트에 있는지 확인 + const clientIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress; + const ipWhitelist = await adminIpWhitelistModel.find({}); + // if () + next(); } catch (e) { res.redirect(frontUrl); } diff --git a/src/route/admin.js b/src/route/admin.js index 337867d6..f85a6f0b 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -8,6 +8,7 @@ const { locationModel, chatModel, reportModel, + adminIpWhitelistModel, } = require("../db/mongo"); let router = express.Router(); @@ -20,7 +21,7 @@ AdminJS.registerAdapter(AdminJSMongoose); // Create router for admin page const adminJsOptions = { - resources: [userModel, roomModel, locationModel, chatModel, reportModel], + resources: [userModel, roomModel, locationModel, chatModel, reportModel, adminIpWhitelistModel], }; const adminJs = new AdminJS(adminJsOptions); router = AdminJSExpress.buildRouter(adminJs, router); From 823563dd770d5a59ae54457c7e65d5c7046d8b1c Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Sat, 14 Jan 2023 21:11:21 +0900 Subject: [PATCH 043/308] Refactor: adminIpWhitelist --- src/db/mongo.js | 8 ++++++-- src/middleware/adminAuth.js | 20 +++++++++++++------- src/route/admin.js | 9 ++++++++- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/db/mongo.js b/src/db/mongo.js index 1cdcb833..ed93de86 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -94,7 +94,8 @@ const reportSchema = Schema({ }); const adminIpWhitelistSchema = Schema({ - ip: { type: String, required: true }, + ip: { type: String, required: true }, // IP 주소 + description: { type: String, default: "" }, // 설명 }); const database = mongoose.connection; @@ -129,5 +130,8 @@ module.exports = { locationModel: mongoose.model("Location", locationSchema), chatModel: mongoose.model("Chat", chatSchema), reportModel: mongoose.model("Report", reportSchema), - adminIpWhitelistModel: mongoose.model("AdminIpWhitelist", adminIpWhitelistSchema), + adminIpWhitelistModel: mongoose.model( + "AdminIpWhitelist", + adminIpWhitelistSchema + ), }; diff --git a/src/middleware/adminAuth.js b/src/middleware/adminAuth.js index 5caa203e..bbf0c309 100644 --- a/src/middleware/adminAuth.js +++ b/src/middleware/adminAuth.js @@ -5,20 +5,26 @@ const { frontUrl } = require("../../security"); const { userModel, adminIpWhitelistModel } = require("../db/mongo"); const adminAuthMiddleware = async (req, res, next) => { - // Check if user is logged in - if (!isLogin(req)) { - return res.redirect(frontUrl); - } try { + // 로그인 여부를 확인 + if (!isLogin(req)) return res.redirect(frontUrl); + // 관리자 유무를 확인 const { id } = getLoginInfo(req); const user = await userModel.findOne({ id }); - if (!user.isAdmin) res.redirect(frontUrl); + if (!user.isAdmin) return res.redirect(frontUrl); // 접속한 IP가 화이트리스트에 있는지 확인 - const clientIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress; + const clientIP = + req.headers["x-forwarded-for"] || req.connection.remoteAddress; const ipWhitelist = await adminIpWhitelistModel.find({}); - // if () + if (!clientIP) return res.redirect(frontUrl); + if ( + ipWhitelist.length > 0 && + ipWhitelist.map((x) => x.ip).indexOf(clientIP) < 0 + ) + return res.redirect(frontUrl); + next(); } catch (e) { res.redirect(frontUrl); diff --git a/src/route/admin.js b/src/route/admin.js index f85a6f0b..1fbddef6 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -21,7 +21,14 @@ AdminJS.registerAdapter(AdminJSMongoose); // Create router for admin page const adminJsOptions = { - resources: [userModel, roomModel, locationModel, chatModel, reportModel, adminIpWhitelistModel], + resources: [ + userModel, + roomModel, + locationModel, + chatModel, + reportModel, + adminIpWhitelistModel, + ], }; const adminJs = new AdminJS(adminJsOptions); router = AdminJSExpress.buildRouter(adminJs, router); From 6ab6e0f5f52b28565a7e6ff7838f6d29babe9d26 Mon Sep 17 00:00:00 2001 From: withsang Date: Sun, 15 Jan 2023 13:29:41 +0900 Subject: [PATCH 044/308] Add: implement token registration api --- .gitignore | 5 ++- src/modules/fcm.js | 81 ++++++++++++++++++------------------- src/route/auth.replace.js | 6 +++ src/service/auth.replace.js | 30 +++++++++++++- 4 files changed, 79 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index 1a4bbe22..6f7fc250 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,7 @@ /logs/*.log # AdminJS 관련 디렉토리 -.adminjs \ No newline at end of file +.adminjs + +# Firebase Admin SDK 크레덴셜 +firebase-admin-sdk-account.json diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 7abd722a..20927ace 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -1,51 +1,50 @@ var admin = require("firebase-admin"); const logger = require("../modules/logger"); -const sendNotificationMultipleUsers = (message, tokens) => { - const tokenMessage = { - ...message, - tokens: tokens, - }; - return admin - .messaging() - .sendMulticast(tokenMessage) - .then((response) => { - return response.failureCount; - }); +const sendNotificationMultipleUsers = async (message, tokens) => { + try { + const tokenMessage = { + ...message, + tokens: tokens, + }; + const { failureCount } = await admin + .messaging() + .sendMulticase(tokenMessage); + return failureCount; + } catch (error) { + logger.error(error); + return -1; + } }; -const sendNotification = (message, token) => { - const tokenMessage = { - ...message, - token: token, - }; - return admin - .messaging() - .send(message) - .then((response) => { - return true; - }) - .catch((error) => { - logger.error(error); - return false; - }); +const sendNotification = async (message, token) => { + try { + const tokenMessage = { + ...message, + token: token, + }; + const response = await admin.messaging.send(tokenMessage); + logger.info(`Notification sent to token ${token}`); + return true; + } catch (error) { + logger.error(error); + return false; + } }; -const sendMessageTopic = (message, topic) => { - const topicMessage = { - ...message, - topic: topic, - }; - return admin - .messaging() - .send(message) - .then((response) => { - return true; - }) - .catch((error) => { - logger.error(error); - return false; - }); +const sendMessageTopic = async (message, topic) => { + try { + const topicMessage = { + ...message, + topic: topic, + }; + const response = await admin.messaging.send(topicMessage); + logger.info(`Notification sent to topic ${topic}`); + return true; + } catch (error) { + logger.error(error); + return false; + } }; module.exports = { diff --git a/src/route/auth.replace.js b/src/route/auth.replace.js index 0eb02edf..760e2d66 100755 --- a/src/route/auth.replace.js +++ b/src/route/auth.replace.js @@ -3,6 +3,7 @@ const router = express.Router(); const authReplaceHandlers = require("../service/auth.replace"); const setTimestamp = require("../middleware/setTimestamp"); +const { authMiddleware } = require("../middleware/auth"); // 로그인 시도 router.route("/try").post(setTimestamp, authReplaceHandlers.tryHandler); @@ -13,4 +14,9 @@ router.route("/sparcssso").get(authReplaceHandlers.sparcsssoHandler); // 로그아웃 router.route("/logout").get(authReplaceHandlers.logoutHandler); +// FCM 토큰 등록 +router + .route("/deviceToken") + .post(authMiddleware, authReplaceHandlers.registerDeviceTokenHandler); + module.exports = router; diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index 664a9ed6..b9836595 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -1,5 +1,5 @@ const security = require("../../security"); -const { userModel } = require("../db/mongo"); +const { userModel, deviceTokenModel } = require("../db/mongo"); const { logout, login } = require("../auth/login"); const { generateNickname, @@ -128,8 +128,36 @@ const logoutHandler = (req, res) => { res.status(200).send("logged out successfully"); }; +const registerDeviceTokenHandler = async (req, res) => { + try { + // FCM 토큰이 현재 유효한지 확인합니다. + const deviceToken = req.body.deviceToken; + try { + // Check if the response is 200. If 400 or 404, then the token is not valid. + const iidInfo = await fetch( + `https://https://iid.googleapis.com/iid/info/${deviceToken}?details=true` + ); + // 데이터베이스에 새 레코드를 추가합니다. + const newDeviceToken = new deviceTokenModel({ + userId: req.session.user_id, + deviceToken, + }); + await newDeviceToken.save(); + return res.status(200).json({ + deviceToken, + }); + } catch (e) { + return res.status(404).send("token not found"); + } + } catch (e) { + logger.error(e); + res.status(500).send("internal server error"); + } +}; + module.exports = { tryHandler, sparcsssoHandler, logoutHandler, + registerDeviceTokenHandler, }; From 1829e6a073f2ae3c5189e2620ea6674e2fa882e0 Mon Sep 17 00:00:00 2001 From: withsang Date: Sun, 15 Jan 2023 13:32:58 +0900 Subject: [PATCH 045/308] Merge origin/dev into #187 branch --- sampleGenerator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sampleGenerator b/sampleGenerator index a8bdc640..374f7b14 160000 --- a/sampleGenerator +++ b/sampleGenerator @@ -1 +1 @@ -Subproject commit a8bdc640346de4bd26594a36bd4a442c04abdf2b +Subproject commit 374f7b14c462e117aa2ed2ae3a718abae39ae527 From 5f0505b06426ed62fc57110003ea596067895df0 Mon Sep 17 00:00:00 2001 From: withsang Date: Sun, 15 Jan 2023 13:50:38 +0900 Subject: [PATCH 046/308] Refactor: separate device token validation --- src/modules/fcm.js | 28 +++++++++++++++++++++++++--- src/service/auth.replace.js | 29 +++++++++++++---------------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 20927ace..f2b6bebc 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -1,6 +1,27 @@ var admin = require("firebase-admin"); const logger = require("../modules/logger"); +/** + * FCM 토큰이 현재 유효한지 IID 엔드포인트에 요청을 보냄으로써 검증하는 함수입니다. + * @param {string} deviceToken - 검증할 FCM 디바이스 토큰입니다. + * @return {Promise} 토큰이 현재 유효한 경우 true, 아닌 경우 false를 반환합니다. + */ +const validateDeviceToken = async (deviceToken) => { + try { + // Check if the status code is 200. If 400 or 404, then the token is not valid. + const response = await fetch( + `https://https://iid.googleapis.com/iid/info/${deviceToken}?details=true` + ); + if (response.status === 200) { + return true; + } else { + return false; + } + } catch (error) { + return false; + } +}; + const sendNotificationMultipleUsers = async (message, tokens) => { try { const tokenMessage = { @@ -48,7 +69,8 @@ const sendMessageTopic = async (message, topic) => { }; module.exports = { - sendNotificationMultipleUsers: sendNotificationMultipleUsers, - sendNotification: sendNotification, - sendMessageTopic: sendMessageTopic, + validateDeviceToken, + sendNotificationMultipleUsers, + sendNotification, + sendMessageTopic, }; diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index b9836595..78e2a64d 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -1,5 +1,6 @@ const security = require("../../security"); const { userModel, deviceTokenModel } = require("../db/mongo"); +const { validateDeviceToken } = require("../modules/fcm"); const { logout, login } = require("../auth/login"); const { generateNickname, @@ -130,25 +131,21 @@ const logoutHandler = (req, res) => { const registerDeviceTokenHandler = async (req, res) => { try { - // FCM 토큰이 현재 유효한지 확인합니다. const deviceToken = req.body.deviceToken; - try { - // Check if the response is 200. If 400 or 404, then the token is not valid. - const iidInfo = await fetch( - `https://https://iid.googleapis.com/iid/info/${deviceToken}?details=true` - ); - // 데이터베이스에 새 레코드를 추가합니다. - const newDeviceToken = new deviceTokenModel({ - userId: req.session.user_id, - deviceToken, - }); - await newDeviceToken.save(); - return res.status(200).json({ - deviceToken, - }); - } catch (e) { + // FCM 토큰이 현재 유효한지 검사합니다. + const isTokenAlive = await validateDeviceToken(deviceToken); + if (!isTokenAlive) { return res.status(404).send("token not found"); } + // 데이터베이스에 새 레코드를 추가합니다. + const newDeviceToken = new deviceTokenModel({ + userId: req.session.user_id, + deviceToken, + }); + await newDeviceToken.save(); + return res.status(200).json({ + deviceToken, + }); } catch (e) { logger.error(e); res.status(500).send("internal server error"); From e5ed2da87e7f4f05959742e6a10e05fbbb769054 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Mon, 16 Jan 2023 22:54:06 +0900 Subject: [PATCH 047/308] Fix: remove async in users.js --- src/service/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/users.js b/src/service/users.js index b2666828..f2ae4c2a 100644 --- a/src/service/users.js +++ b/src/service/users.js @@ -54,7 +54,7 @@ const editNicknameHandler = (req, res) => { }); }; -const editAccountHandler = async (req, res) => { +const editAccountHandler = (req, res) => { const newAccount = req.body.account; // 계좌번호를 갱신하고 결과를 반환 From c3ffd5fdfabf6f3e0dc7c8b3f848b4d5dc6fa6fc Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Mon, 16 Jan 2023 23:05:48 +0900 Subject: [PATCH 048/308] Test: add account in logininfo.js --- src/service/logininfo.js | 2 +- test/logininfo.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/service/logininfo.js b/src/service/logininfo.js index 4c705732..056b6072 100644 --- a/src/service/logininfo.js +++ b/src/service/logininfo.js @@ -28,7 +28,7 @@ const detailHandler = (req, res) => { subinfo: result.subinfo, email: result.email, profileImgUrl: result.profileImageUrl, - account: result.account, + account: result.account ? result.account : "", }); } } diff --git a/test/logininfo.js b/test/logininfo.js index 9f128995..6f2ab0ff 100644 --- a/test/logininfo.js +++ b/test/logininfo.js @@ -106,6 +106,7 @@ describe("[logininfo] 2.detailHandler", () => { subinfo: result.subinfo, email: result.email, profileImgUrl: result.profileImageUrl, + account: "", }); }, }; From f4b0fb45798bddc5824f798d60fc48f7df7852f2 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Tue, 17 Jan 2023 15:20:45 +0900 Subject: [PATCH 049/308] Fix: adminIPWhitelistModel --- src/db/mongo.js | 8 ++++---- src/middleware/adminAuth.js | 4 ++-- src/route/admin.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/db/mongo.js b/src/db/mongo.js index ed93de86..8b1f3183 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -93,7 +93,7 @@ const reportSchema = Schema({ time: { type: Date, required: true }, }); -const adminIpWhitelistSchema = Schema({ +const adminIPWhitelistSchema = Schema({ ip: { type: String, required: true }, // IP 주소 description: { type: String, default: "" }, // 설명 }); @@ -130,8 +130,8 @@ module.exports = { locationModel: mongoose.model("Location", locationSchema), chatModel: mongoose.model("Chat", chatSchema), reportModel: mongoose.model("Report", reportSchema), - adminIpWhitelistModel: mongoose.model( - "AdminIpWhitelist", - adminIpWhitelistSchema + adminIPWhitelistModel: mongoose.model( + "AdminIPWhitelist", + adminIPWhitelistSchema ), }; diff --git a/src/middleware/adminAuth.js b/src/middleware/adminAuth.js index bbf0c309..d6bb6d07 100644 --- a/src/middleware/adminAuth.js +++ b/src/middleware/adminAuth.js @@ -2,7 +2,7 @@ const { isLogin, getLoginInfo } = require("../auth/login"); const { frontUrl } = require("../../security"); -const { userModel, adminIpWhitelistModel } = require("../db/mongo"); +const { userModel, adminIPWhitelistModel } = require("../db/mongo"); const adminAuthMiddleware = async (req, res, next) => { try { @@ -17,7 +17,7 @@ const adminAuthMiddleware = async (req, res, next) => { // 접속한 IP가 화이트리스트에 있는지 확인 const clientIP = req.headers["x-forwarded-for"] || req.connection.remoteAddress; - const ipWhitelist = await adminIpWhitelistModel.find({}); + const ipWhitelist = await adminIPWhitelistModel.find({}); if (!clientIP) return res.redirect(frontUrl); if ( ipWhitelist.length > 0 && diff --git a/src/route/admin.js b/src/route/admin.js index 1fbddef6..816d5f4b 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -8,7 +8,7 @@ const { locationModel, chatModel, reportModel, - adminIpWhitelistModel, + adminIPWhitelistModel, } = require("../db/mongo"); let router = express.Router(); @@ -27,7 +27,7 @@ const adminJsOptions = { locationModel, chatModel, reportModel, - adminIpWhitelistModel, + adminIPWhitelistModel, ], }; const adminJs = new AdminJS(adminJsOptions); From 1ba303d45074a76a3d22812c54af30bf48a8e3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9C=EC=99=84?= Date: Wed, 18 Jan 2023 00:13:08 +0900 Subject: [PATCH 050/308] Refactor: fix maxTime --- src/service/rooms.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/service/rooms.js b/src/service/rooms.js index 90d592ca..242145d8 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -18,15 +18,8 @@ const createHandler = async (req, res) => { }); } - const createTime = new Date(time); - createTime.setHours(0, 0, 0, 0); - - const currentTime = new Date(); - currentTime.setHours(0, 0, 0, 0); - - const maxTime = new Date(currentTime); + const maxTime = new Date(); maxTime.setDate(currentTime.getDate() + 14); - maxTime.setHours(0, 0, 0, 0); if (createTime.getTime() > maxTime.getTime()) { From a7ef18e8a9117c4fad6d8e16861a22a632b47447 Mon Sep 17 00:00:00 2001 From: ptptsw Date: Wed, 18 Jan 2023 00:15:36 +0900 Subject: [PATCH 051/308] Refactor: fix maxTime --- src/service/rooms.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/service/rooms.js b/src/service/rooms.js index 90d592ca..242145d8 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -18,15 +18,8 @@ const createHandler = async (req, res) => { }); } - const createTime = new Date(time); - createTime.setHours(0, 0, 0, 0); - - const currentTime = new Date(); - currentTime.setHours(0, 0, 0, 0); - - const maxTime = new Date(currentTime); + const maxTime = new Date(); maxTime.setDate(currentTime.getDate() + 14); - maxTime.setHours(0, 0, 0, 0); if (createTime.getTime() > maxTime.getTime()) { From 80388deeaa8f1849f86300953f61c1be2183ae06 Mon Sep 17 00:00:00 2001 From: ptptsw Date: Wed, 18 Jan 2023 00:18:24 +0900 Subject: [PATCH 052/308] Refactor: fix currentTime --- src/service/rooms.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/service/rooms.js b/src/service/rooms.js index 242145d8..59d06d94 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -18,6 +18,7 @@ const createHandler = async (req, res) => { }); } + const currentTime = new Date(); const maxTime = new Date(); maxTime.setDate(currentTime.getDate() + 14); maxTime.setHours(0, 0, 0, 0); From eaf77ee35a8e5defcf9dbb6bcc8d4818fef247d8 Mon Sep 17 00:00:00 2001 From: ptptsw Date: Wed, 18 Jan 2023 00:21:48 +0900 Subject: [PATCH 053/308] Refactor: fix << --- src/service/rooms.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/service/rooms.js b/src/service/rooms.js index bf7fd4d9..59d06d94 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -18,10 +18,7 @@ const createHandler = async (req, res) => { }); } -<<<<<<< HEAD const currentTime = new Date(); -======= ->>>>>>> 1ba303d45074a76a3d22812c54af30bf48a8e3d3 const maxTime = new Date(); maxTime.setDate(currentTime.getDate() + 14); maxTime.setHours(0, 0, 0, 0); From 9ab1c954dc5e181b68027e18f5a8278d4b309d40 Mon Sep 17 00:00:00 2001 From: ptptsw Date: Wed, 18 Jan 2023 22:08:07 +0900 Subject: [PATCH 054/308] Refactor: fix maxTime --- src/service/rooms.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/service/rooms.js b/src/service/rooms.js index 59d06d94..b9532ee3 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -18,9 +18,11 @@ const createHandler = async (req, res) => { }); } - const currentTime = new Date(); + const createTime = new Date(time); + createTime.setHours(0, 0, 0, 0); + const maxTime = new Date(); - maxTime.setDate(currentTime.getDate() + 14); + maxTime.setDate(maxTime.getDate() + 14); maxTime.setHours(0, 0, 0, 0); if (createTime.getTime() > maxTime.getTime()) { From 9bec752d82f060948adae4574212055abbb3d2ec Mon Sep 17 00:00:00 2001 From: ptptsw Date: Wed, 18 Jan 2023 22:13:10 +0900 Subject: [PATCH 055/308] Feature: add new chat type --- src/db/mongo.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/db/mongo.js b/src/db/mongo.js index 557403eb..9bd1dbcf 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -74,7 +74,10 @@ const locationSchema = Schema({ }); const chatSchema = Schema({ roomId: { type: Schema.Types.ObjectId, ref: "Room", required: true }, - type: { type: String, enum: ["text", "in", "out", "s3img"] }, // 메시지 종류 + type: { + type: String, + enum: ["text", "in", "out", "s3img", "payment", "settlement"], + }, // 메시지 종류 authorId: { type: Schema.Types.ObjectId, ref: "User", required: true }, // 작성자 id content: { type: String, default: "" }, time: { type: Date, required: true }, From abf9bc030298b59e95b53e5561f59f605e6bf846 Mon Sep 17 00:00:00 2001 From: ptptsw Date: Wed, 18 Jan 2023 22:54:05 +0900 Subject: [PATCH 056/308] Feature: emit payment,settlement --- src/route/chats.socket.js | 14 ++++++++++++-- src/service/rooms.js | 12 ++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 67337745..10c2e61a 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -39,7 +39,12 @@ const emitChatEvent = async (io, roomId, chat) => { chat.authorName = author.nickname; chat.authorProfileUrl = author.profileImageUrl; - if (chat.type == "in" || chat.type == "out") { + if ( + chat.type == "in" || + chat.type == "out" || + chat.type == "payment" || + chat.type == "settlement" + ) { const userIds = chat.content.split("|"); chat.inOutNames = []; for (const userId of userIds) { @@ -68,7 +73,12 @@ const transformChatsForRoom = async (chats) => { for (const chat of chats) { // inOutNames 배열(들어오거나 나간 사용자들의 닉네임으로 이루어진 배열)을 생성합니다. chat.inOutNames = []; - if (chat.type === "in" || chat.type === "out") { + if ( + chat.type === "in" || + chat.type === "out" || + chat.type === "payment" || + chat.type === "settlement" + ) { const inOutUserIds = chat.content.split("|"); chat.inOutNames = await Promise.all( inOutUserIds.map(async (userId) => { diff --git a/src/service/rooms.js b/src/service/rooms.js index 10d448a3..c74dcc21 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -422,6 +422,12 @@ const commitPaymentHandler = async (req, res) => { await user.save(); + await emitChatEvent(req.app.get("io"), roomId, { + type: "payment", + content: user.id, + authorId: user._id, + }); + // 수정한 방 정보를 반환합니다. res.send(formatSettlement(roomObject, { isOver: true })); } catch (err) { @@ -480,6 +486,12 @@ const settlementHandler = async (req, res) => { await user.save(); + await emitChatEvent(req.app.get("io"), roomId, { + type: "settlement", + content: user.id, + authorId: user._id, + }); + // 수정한 방 정보를 반환합니다. res.send(formatSettlement(roomObject, { isOver: true })); } catch (err) { From 33b294319ea12ba5e6b52f40eb64be2b1e5e0997 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Thu, 19 Jan 2023 15:49:49 +0900 Subject: [PATCH 057/308] Add: admin.logs.js --- app.js | 5 +++-- src/route/admin.logs.js | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/route/admin.logs.js diff --git a/app.js b/app.js index 26c5aff3..d682cc28 100644 --- a/app.js +++ b/app.js @@ -26,6 +26,7 @@ app.use(cookieParser()); app.use(require("response-time")(logAPIAccess)); // admin 페이지는 rate limiting을 적용하지 않습니다. +app.use("/admin/logs", require("./src/route/admin.logs")); app.use("/admin", require("./src/route/admin")); // Apply the rate limiting middleware to all requests @@ -34,9 +35,9 @@ app.use(require("./src/middleware/limitRate")); // 라우터 및 리액트 // /rooms/v2에 요청을 보내는 기존 클라이언트 코드 호환성 유지 app.use("/auth", require("./src/route/auth")); -app.use("/json/logininfo", require("./src/route/logininfo")); +app.use(["logininfo", "/json/logininfo"], require("./src/route/logininfo")); app.use("/users", require("./src/route/users")); -app.use(["/rooms/v2", "/rooms"], require("./src/route/rooms")); +app.use(["/rooms", "/rooms/v2"], require("./src/route/rooms")); app.use("/chats", require("./src/route/chats")); app.use("/locations", require("./src/route/locations")); app.use("/reports", require("./src/route/reports")); diff --git a/src/route/admin.logs.js b/src/route/admin.logs.js new file mode 100644 index 00000000..98cebcbe --- /dev/null +++ b/src/route/admin.logs.js @@ -0,0 +1,11 @@ +const express = require("express"); + +const router = express.Router(); + +// Requires admin property of the user to enter admin page. +router.use(require("../middleware/adminAuth")); + +// Log 파일 제공 +router.use(express.static(__dirname + '/logs')); + +module.exports = router; From 75726535552c5f8c44539fb3ababf4482e39f0e7 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Thu, 19 Jan 2023 19:28:43 +0900 Subject: [PATCH 058/308] Add: sort option to searchByUserHandler --- src/service/rooms.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/service/rooms.js b/src/service/rooms.js index b9532ee3..94e4548f 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -356,6 +356,7 @@ const searchByUserHandler = async (req, res) => { try { const user = await userModel .findOne({ id: req.userId }) + .sort({ time: -1 }) .populate({ path: "ongoingRoom", options: { limit: 1000 }, From 33c11d79b6587bc54d3c56a93164ca460e7c494b Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Thu, 19 Jan 2023 19:55:06 +0900 Subject: [PATCH 059/308] Add: ssoLogoutUrl to logoutHandler --- src/service/auth.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/service/auth.js b/src/service/auth.js index 4ebfb4b3..1a24fa5c 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -153,8 +153,15 @@ const createNewTokenHandler = (req, res, userData) => { }; const logoutHandler = (req, res) => { - logout(req, res); - res.status(200).send("logged out successfully"); + try { + const { sid } = getLoginInfo(req); + const redirectUrl = security.frontUrl + "/login"; + const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); + logout(req, res); + res.json({ ssoLogoutUrl }); + } catch (e) { + res.status(500).send("Auth/logout : internal server error"); + } }; module.exports = { From 39135e43db6afc00ac740614e406e1f2d04adff2 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Fri, 20 Jan 2023 09:33:12 +0900 Subject: [PATCH 060/308] Fix: admin.logs.js --- app.js | 4 ++-- src/route/admin.logs.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index d682cc28..32930230 100644 --- a/app.js +++ b/app.js @@ -35,9 +35,9 @@ app.use(require("./src/middleware/limitRate")); // 라우터 및 리액트 // /rooms/v2에 요청을 보내는 기존 클라이언트 코드 호환성 유지 app.use("/auth", require("./src/route/auth")); -app.use(["logininfo", "/json/logininfo"], require("./src/route/logininfo")); +app.use(["/logininfo", "/json/logininfo"], require("./src/route/logininfo")); app.use("/users", require("./src/route/users")); -app.use(["/rooms", "/rooms/v2"], require("./src/route/rooms")); +app.use(["/rooms/v2", "/rooms"], require("./src/route/rooms")); app.use("/chats", require("./src/route/chats")); app.use("/locations", require("./src/route/locations")); app.use("/reports", require("./src/route/reports")); diff --git a/src/route/admin.logs.js b/src/route/admin.logs.js index 98cebcbe..d3ca8bb1 100644 --- a/src/route/admin.logs.js +++ b/src/route/admin.logs.js @@ -1,4 +1,5 @@ const express = require("express"); +const path = require("path"); const router = express.Router(); @@ -6,6 +7,6 @@ const router = express.Router(); router.use(require("../middleware/adminAuth")); // Log 파일 제공 -router.use(express.static(__dirname + '/logs')); +router.use(express.static(path.join(process.env.PWD, "logs"))); module.exports = router; From cd9469d5118ac4eb96ed7e3d4194f5d00cd80f39 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Fri, 20 Jan 2023 15:14:46 +0900 Subject: [PATCH 061/308] Fix: searchByUserHandler sort option --- src/service/rooms.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/service/rooms.js b/src/service/rooms.js index 94e4548f..c1fce152 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -356,15 +356,22 @@ const searchByUserHandler = async (req, res) => { try { const user = await userModel .findOne({ id: req.userId }) - .sort({ time: -1 }) .populate({ path: "ongoingRoom", - options: { limit: 1000 }, + options: { + limit: 1000, + // ongoingRoom 은 시간 오름차순 정렬 + sort: { time: 1 }, + }, populate: roomPopulateOption, }) .populate({ path: "doneRoom", - options: { limit: 1000 }, + options: { + limit: 1000, + // doneRoom 은 시간 내림차순 정렬 + sort: { time: -1 }, + }, populate: roomPopulateOption, }) .lean(); From 7c42297ca217d647c4860e93c33e988fd8b3dcbf Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Fri, 20 Jan 2023 19:55:00 +0900 Subject: [PATCH 062/308] Fix: /logout api document --- src/route/docs/auth.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/route/docs/auth.md b/src/route/docs/auth.md index 133d2017..799ddd01 100644 --- a/src/route/docs/auth.md +++ b/src/route/docs/auth.md @@ -49,14 +49,13 @@ ```javascript { - status: 200, - data: "logged out successfully", + ssoLogoutUrl: String, // sso 로그아웃 url } ``` #### Errors -- 없음 +- 500 / "internal server error" ### `/getToken` **(GET)** From a00fd86669fd59af39ce99ae14b36ce57e4c3520 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Sat, 21 Jan 2023 09:47:10 +0900 Subject: [PATCH 063/308] Fix: logoutHandler --- src/service/auth.replace.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index 664a9ed6..10b9119f 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -124,8 +124,13 @@ const sparcsssoHandler = (req, res) => { }; const logoutHandler = (req, res) => { - logout(req, res); - res.status(200).send("logged out successfully"); + try { + const ssoLogoutUrl = security.frontUrl + "/login"; + logout(req, res); + res.json({ ssoLogoutUrl }); + } catch (e) { + res.status(500).send("Auth/logout : internal server error"); + } }; module.exports = { From 61975b0a6e97bcd673033612f27f1578d205c7d1 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 22 Jan 2023 17:37:03 +0900 Subject: [PATCH 064/308] Test: update req of rooms.js --- test/rooms.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/rooms.js b/test/rooms.js index d97da381..49b428ee 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -133,6 +133,7 @@ describe("[rooms] 6.commitPaymentHandler", () => { body: { roomId: testRoom._id }, userId: testUser1.id, timestamp: Date.now() + 60 * 1000, + app, }; const res = { send: (data) => { @@ -153,6 +154,7 @@ describe("[rooms] 7.settlementHandler", () => { const req = { body: { roomId: testRoom._id }, userId: testUser2.id, + app, }; const res = { send: (data) => { From 12f6414aa20b9cbbd721b581f02252b68826db8c Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Sun, 22 Jan 2023 18:00:37 +0900 Subject: [PATCH 065/308] Refactor: auth.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그아웃 실패 시 SSO 로그아웃도 같이함 --- src/service/auth.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/service/auth.js b/src/service/auth.js index 1a24fa5c..5fc6bb1c 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -83,8 +83,8 @@ const loginDone = (req, res, userData) => { ); }; -const loginFalse = (req, res) => { - res.redirect(security.frontUrl + "/login/false"); // 리엑트로 연결되나? +const loginFalse = (req, res, redirectUrl = "") => { + res.redirect(redirectUrl || security.frontUrl + "/login/false"); }; const generateTokenHandler = (req, res) => { @@ -114,7 +114,14 @@ const sparcsssoCallbackHandler = (req, res) => { } else { loginDone(req, res, userData); } - } else loginFalse(req, res); + } else { + // SSO 로그아웃 URL 생성 + const { sid } = userData; + const redirectUrl = security.frontUrl + "/login/false"; + const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); + + loginFalse(req, res, ssoLogoutUrl); + } }); } }; From 98e6794dac1b42b627e22eedc32322d12a4dca2f Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Sun, 22 Jan 2023 18:11:01 +0900 Subject: [PATCH 066/308] Refactor: rename loginFail --- src/service/auth.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/service/auth.js b/src/service/auth.js index 5fc6bb1c..91c3ef9d 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -54,7 +54,7 @@ const joinus = (req, res, userData) => { }); newUser.save((err) => { if (err) { - loginFalse(req, res); + loginFail(req, res); return; } loginDone(req, res, userData); @@ -72,7 +72,7 @@ const loginDone = (req, res, userData) => { { id: userData.id }, "name id withdraw ban", (err, result) => { - if (err) loginFalse(req, res); + if (err) loginFail(req, res); else if (!result) joinus(req, res, userData); else if (result.name != userData.name) update(req, res, userData); else { @@ -83,8 +83,8 @@ const loginDone = (req, res, userData) => { ); }; -const loginFalse = (req, res, redirectUrl = "") => { - res.redirect(redirectUrl || security.frontUrl + "/login/false"); +const loginFail = (req, res, redirectUrl = "") => { + res.redirect(redirectUrl || security.frontUrl + "/login?status=fail"); }; const generateTokenHandler = (req, res) => { @@ -103,7 +103,7 @@ const sparcsssoCallbackHandler = (req, res) => { const state1 = req.session.state; const state2 = req.body.state || req.query.state; - if (state1 !== state2) loginFalse(req, res); + if (state1 !== state2) loginFail(req, res); else { const code = req.body.code || req.query.code; client.getUserInfo(code).then((userDataBefore) => { @@ -117,10 +117,10 @@ const sparcsssoCallbackHandler = (req, res) => { } else { // SSO 로그아웃 URL 생성 const { sid } = userData; - const redirectUrl = security.frontUrl + "/login/false"; + const redirectUrl = security.frontUrl + "/login?status=fail"; const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); - loginFalse(req, res, ssoLogoutUrl); + loginFail(req, res, ssoLogoutUrl); } }); } @@ -133,7 +133,7 @@ const createNewTokenHandler = (req, res, userData) => { async (err, result) => { if (err) { logger.error(err); - loginFalse(req, res); + loginFail(req, res); } else if (!result) joinus(req, res, userData); else if (result.name !== userData.name) update(req, res, userData); else { From c407391ca722d5f50cb7ea90b6edea2911d50966 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Sun, 22 Jan 2023 18:12:12 +0900 Subject: [PATCH 067/308] Refactor: auth.js --- src/service/auth.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/service/auth.js b/src/service/auth.js index 91c3ef9d..ed7dad8a 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -115,11 +115,10 @@ const sparcsssoCallbackHandler = (req, res) => { loginDone(req, res, userData); } } else { - // SSO 로그아웃 URL 생성 + // 카이스트 구성원이 아닌 경우, SSO 로그아웃 이후, 로그인 실패 URI 로 이동합니다 const { sid } = userData; const redirectUrl = security.frontUrl + "/login?status=fail"; const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); - loginFail(req, res, ssoLogoutUrl); } }); From ce74b1ae0481b7587738062ed954719db3784987 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 22 Jan 2023 22:16:45 +0900 Subject: [PATCH 068/308] Test: remove unused variable in users.js --- test/users.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/users.js b/test/users.js index 2472e88b..d7244a28 100644 --- a/test/users.js +++ b/test/users.js @@ -1,7 +1,6 @@ const expect = require("chai").expect; -const express = require("express"); const usersHandlers = require("../src/service/users"); -const { userModel, roomModel, locationModel } = require("../src/db/mongo"); +const { userModel } = require("../src/db/mongo"); const { userGenerator, testRemover } = require("./utils"); let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; @@ -70,7 +69,7 @@ describe("[users] 3.editNicknameHandler", () => { }); }); -// 4. 방의 정보를 통해 검색 +// 4. Image PUrl이 제대로 변경 되었는지 확인 describe("[users] 4.editProfileImgGetPUrlHandler", () => { it("should return url and fields of data", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); From 16a7df601b875fbc026a5e78f7658555db3deeee Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 25 Jan 2023 22:52:58 +0900 Subject: [PATCH 069/308] Fix: loginFail redirect url --- src/service/auth.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/service/auth.js b/src/service/auth.js index ed7dad8a..c0a3a60a 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -84,7 +84,7 @@ const loginDone = (req, res, userData) => { }; const loginFail = (req, res, redirectUrl = "") => { - res.redirect(redirectUrl || security.frontUrl + "/login?status=fail"); + res.redirect(redirectUrl || security.frontUrl + "/login/fail"); }; const generateTokenHandler = (req, res) => { @@ -108,7 +108,8 @@ const sparcsssoCallbackHandler = (req, res) => { const code = req.body.code || req.query.code; client.getUserInfo(code).then((userDataBefore) => { const userData = transUserData(userDataBefore); - if (userData.isEligible || security.nodeEnv !== "production") { + if (userData.isEligible) { + // || security.nodeEnv !== "production") { if (req.session.isApp) { createNewTokenHandler(req, res, userData); } else { @@ -117,7 +118,7 @@ const sparcsssoCallbackHandler = (req, res) => { } else { // 카이스트 구성원이 아닌 경우, SSO 로그아웃 이후, 로그인 실패 URI 로 이동합니다 const { sid } = userData; - const redirectUrl = security.frontUrl + "/login?status=fail"; + const redirectUrl = security.frontUrl + "/login/fail"; const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); loginFail(req, res, ssoLogoutUrl); } From adb93c456d71dec151d8e76e58f284700cef6540 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Thu, 26 Jan 2023 08:20:34 +0900 Subject: [PATCH 070/308] Fix: sparcsssoCallbackHandler --- src/service/auth.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/service/auth.js b/src/service/auth.js index c0a3a60a..5fdf0283 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -108,8 +108,7 @@ const sparcsssoCallbackHandler = (req, res) => { const code = req.body.code || req.query.code; client.getUserInfo(code).then((userDataBefore) => { const userData = transUserData(userDataBefore); - if (userData.isEligible) { - // || security.nodeEnv !== "production") { + if (userData.isEligible || security.nodeEnv !== "production") { if (req.session.isApp) { createNewTokenHandler(req, res, userData); } else { From ba9c1f7073e12c9fcd72db28deb281e86109abe8 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 31 Jan 2023 15:46:05 +0000 Subject: [PATCH 071/308] Refactor: use httpmock --- package-lock.json | 53 ++++++++++++++++++ package.json | 1 + src/service/users.js | 12 ++-- test/users.js | 129 ++++++++++++++++++++++++------------------- 4 files changed, 133 insertions(+), 62 deletions(-) diff --git a/package-lock.json b/package-lock.json index 93a20f06..2850840e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "express-validator": "^6.14.0", "jsonwebtoken": "^8.5.1", "mongoose": "^6.5.2", + "node-mocks-http": "^1.12.1", "querystring": "^0.2.1", "redis": "^4.2.0", "response-time": "^2.3.2", @@ -6889,6 +6890,34 @@ "node": ">= 0.6" } }, + "node_modules/node-mocks-http": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.1.tgz", + "integrity": "sha512-jrA7Sn3qI6GsHgWtUW3gMj0vO6Yz0nJjzg3jRZYjcfj4tzi8oWPauDK1qHVJoAxTbwuDHF1JiM9GISZ/ocI/ig==", + "dependencies": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/node-mocks-http/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -14446,6 +14475,30 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "node-mocks-http": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.1.tgz", + "integrity": "sha512-jrA7Sn3qI6GsHgWtUW3gMj0vO6Yz0nJjzg3jRZYjcfj4tzi8oWPauDK1qHVJoAxTbwuDHF1JiM9GISZ/ocI/ig==", + "requires": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + } + } + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", diff --git a/package.json b/package.json index f6ecb4c8..46aa8ef5 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "express-validator": "^6.14.0", "jsonwebtoken": "^8.5.1", "mongoose": "^6.5.2", + "node-mocks-http": "^1.12.1", "querystring": "^0.2.1", "redis": "^4.2.0", "response-time": "^2.3.2", diff --git a/src/service/users.js b/src/service/users.js index dea3f246..d16609ac 100644 --- a/src/service/users.js +++ b/src/service/users.js @@ -8,7 +8,7 @@ const agreeOnTermsOfServiceHandler = async (req, res) => { if (user.agreeOnTermsOfService !== true) { user.agreeOnTermsOfService = true; await user.save(); - res.send( + res.status(200).send( "User/agreeOnTermsOfService : agree on Terms of Service successful" ); } else { @@ -31,15 +31,15 @@ const getAgreeOnTermsOfServiceHandler = async (req, res) => { } }; -const editNicknameHandler = (req, res) => { +const editNicknameHandler = async (req, res) => { const newNickname = req.body.nickname; // 닉네임을 갱신하고 결과를 반환 - userModel + await userModel .findOneAndUpdate({ id: req.userId }, { nickname: newNickname }) .then((result) => { if (result) { - res.send("User/editNickname : edit user nickname successful"); + res.status(200).send("User/editNickname : edit user nickname successful"); } else { res.status(400).send("User/editNickname : such user id does not exist"); } @@ -50,11 +50,11 @@ const editNicknameHandler = (req, res) => { }); }; -const editAccountHandler = (req, res) => { +const editAccountHandler = async (req, res) => { const newAccount = req.body.account; // 계좌번호를 갱신하고 결과를 반환 - userModel + await userModel .findOneAndUpdate({ id: req.userId }, { account: newAccount }) .then((result) => { if (result) { diff --git a/test/users.js b/test/users.js index d7244a28..376bca3b 100644 --- a/test/users.js +++ b/test/users.js @@ -2,111 +2,128 @@ const expect = require("chai").expect; const usersHandlers = require("../src/service/users"); const { userModel } = require("../src/db/mongo"); const { userGenerator, testRemover } = require("./utils"); +const httpMocks = require("node-mocks-http"); let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; // users.js 관련 5개의 handler을 테스트 -// 1. test1 유저를 생성 후, agreeOnTermsOfServiceHandler가 제대로 send 하는지 확인 +// 1. test1 유저를 생성 후, agreeOnTermsOfServiceHandler가 제대로 msg를 send 하는지 확인 describe("[users] 1.agreeOnTermsOfServiceHandler", () => { it("should return correct response from handler", async () => { let testUser1 = await userGenerator("test1", testData); const msg = "User/agreeOnTermsOfService : agree on Terms of Service successful"; - const req = { + let req = httpMocks.createRequest({ userId: testUser1.id, - }; - const res = { - send: (data) => { - expect(data).to.equal(msg); - }, - }; - + }); + let res = httpMocks.createResponse(); await usersHandlers.agreeOnTermsOfServiceHandler(req, res); + + expect(res).to.has.property("statusCode", 200); + expect(res._getData()).to.equal(msg); }); }); -// 2. test1 유저의 agreeOnTermsOfService 정보를 가져옴 +// 2. test1 유저의 agreeOnTermsOfService 정보를 가져와서 true인지 확인 describe("[users] 2.getAgreeOnTermsOfServiceHandler", () => { it("should return AgreeOnTermsOfService of user", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); - const req = { + let req = httpMocks.createRequest({ userId: testUser1.id, - }; - const res = { - json: (data) => { - expect(data).to.has.property("agreeOnTermsOfService", true); - }, - }; - + }); + let res = httpMocks.createResponse(); await usersHandlers.getAgreeOnTermsOfServiceHandler(req, res); + + expect(res).to.has.property("statusCode", 200); + expect(res._getJSONData()).to.has.property("agreeOnTermsOfService", true); }); }); -// 3. test1의 nickname을 test-nickname으로 변경 +// 3. test1 유저의 nickname을 test-nickname으로 변경, 성공 메세지가 제대로 오는지 확인 describe("[users] 3.editNicknameHandler", () => { + const testNickname = "test-nickname"; + it("should return correct response from handler", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); - const req = { + const msg = "User/editNickname : edit user nickname successful"; + let req = httpMocks.createRequest({ userId: testUser1.id, body: { - nickname: "test-nickname", + nickname: testNickname, }, - }; - const res = { - send: (data) => { - expect(data).to.equal( - "User/editNickname : edit user nickname successful" - ); - }, - }; + }); + let res = httpMocks.createResponse(); + await usersHandlers.editNicknameHandler(req, res); - usersHandlers.editNicknameHandler(req, res); + expect(res).to.has.property("statusCode", 200); + expect(res._getData()).to.equal(msg); }); it("should be changed to new nickname", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); - expect(testUser1).to.have.property("nickname", "test-nickname"); + expect(testUser1).to.have.property("nickname", testNickname); }); }); -// 4. Image PUrl이 제대로 변경 되었는지 확인 -describe("[users] 4.editProfileImgGetPUrlHandler", () => { - it("should return url and fields of data", async () => { +// 3. test1 유저의 계좌번호를 testAccount으로 변경, 성공 메세지가 제대로 오는지 확인 +describe("[users] 4.editAccountHandler", () => { + const testAccount = "신한 0123456789012"; + + it("should return correct response from handler", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); - const req = { + const msg = "User/editAccount : edit user account successful"; + let req = httpMocks.createRequest({ userId: testUser1.id, body: { - type: "image/jpg", - }, - }; - const res = { - json: (data) => { - expect(data).to.have.property("url"); - expect(data.fields).to.have.property( - "key", - `profile-img/${testUser1._id}` - ); + account: testAccount, }, - }; + }); + let res = httpMocks.createResponse(); + await usersHandlers.editAccountHandler(req, res); + expect(res).to.has.property("statusCode", 200); + expect(res._getData()).to.equal(msg); + }); + + it("should be changed to new account", async () => { + const testUser1 = await userModel.findOne({ id: "test1" }); + expect(testUser1).to.have.property("account", testAccount); + }); +}); + +// 5. test1 유저의 프로필 업로드를 위한 PUrl을 제대로 받았는지 확인 +describe("[users] 5.editProfileImgGetPUrlHandler", () => { + it("should return url and fields of data", async () => { + const testUser1 = await userModel.findOne({ id: "test1" }); + const testImgType = "image/jpg" + let req = httpMocks.createRequest({ + userId: testUser1.id, + body: { + type: testImgType, + }, + }); + let res = httpMocks.createResponse(); await usersHandlers.editProfileImgGetPUrlHandler(req, res); + + expect(res).to.has.property("statusCode", 200); + expect(res._getJSONData()).to.has.property("url"); + expect(res._getJSONData().fields).to.has.property("key", + `profile-img/${testUser1._id}`); + expect(res._getJSONData().fields).to.has.property("Content-Type", + testImgType); }); }); -// 5. test1 user의 profileImageUrl 주소를 변경 -describe("[users] 5.editProfileImgDoneHandler", () => { +// 6. test1 유저의 프로필 업로드가 정상적으로 완료되었는지 확인 +describe("[users] 6.editProfileImgDoneHandler", () => { it("should return correct result and new profileImageUrl", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); - const req = { + let req = httpMocks.createRequest({ userId: testUser1.id, - }; - const res = { - json: (data) => { - expect(data).to.have.property("result", true); - expect(data).to.have.property("profileImageUrl", testUser1._id); - }, - }; + }); + let res = httpMocks.createResponse(); await usersHandlers.editProfileImgDoneHandler(req, res); + expect(res).to.has.property("statusCode", 200); }); }); From 877efa1c227103e62239a4d3b47770286cfbf952 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Wed, 1 Feb 2023 18:56:44 +0900 Subject: [PATCH 072/308] Add swagger --- src/middleware/swagger.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/middleware/swagger.js diff --git a/src/middleware/swagger.js b/src/middleware/swagger.js new file mode 100644 index 00000000..82230813 --- /dev/null +++ b/src/middleware/swagger.js @@ -0,0 +1,27 @@ +const express = require('express'); +const swaggerUi = require('swagger-ui-express'); +const swaggereJsdoc = require('swagger-jsdoc'); + +// https://github.com/Surnet/swagger-jsdoc + +const swaggerProvider = (options) => { + const router = express.Router(); + const options = { + definition: { + info: { + title: 'Test API', + version: '1.0.0', + description: 'Test API with express', + }, + basePath: '/', + ...options, + }, + apis: ['./routes/*.js'] + }; + + router.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggereJsdoc(options))); + + return router; +} + +module.exports = swaggerProvider; From 76e38efcb349a8268431aba43742e39cd451cc5c Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 1 Feb 2023 22:23:59 +0900 Subject: [PATCH 073/308] Refactor: security.js --- security.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/security.js b/security.js index 0511ffe6..87b1bf13 100644 --- a/security.js +++ b/security.js @@ -1,21 +1,22 @@ -require("dotenv").config({ path: `./.env.${process.env.NODE_ENV}` }); // 환경 변수에 따라 .env.production/.env.development 파일을 읽어옴 +// 환경 변수에 따라 .env.production 또는 .env.development 파일을 읽어옴 +require("dotenv").config({ path: `./.env.${process.env.NODE_ENV}` }); module.exports = { nodeEnv: process.env.NODE_ENV, - mongo: process.env.DB_PATH, - session: process.env.SESSION_KEY, - redis: process.env.REDIS_PATH, + mongo: process.env.DB_PATH, // required + session: process.env.SESSION_KEY || "TAXI_SESSION_KEY", // optional + redis: process.env.REDIS_PATH, // optional sparcssso: { - id: process.env.SPARCSSSO_CLIENT_ID, - key: process.env.SPARCSSSO_CLIENT_KEY, + id: process.env.SPARCSSSO_CLIENT_ID || "", // optional + key: process.env.SPARCSSSO_CLIENT_KEY || "", // optional }, - port: process.env.PORT, - frontUrl: process.env.FRONT_URL, + port: process.env.PORT || 80, // optional (default = 80) + frontUrl: process.env.FRONT_URL || "http://localhost:3000", // optional (default = "http://localhost:3000") aws: { - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, - s3BucketName: process.env.AWS_S3_BUCKET_NAME, + accessKeyId: process.env.AWS_ACCESS_KEY_ID, // required + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, // required + s3BucketName: process.env.AWS_S3_BUCKET_NAME, // required }, - jwtSecretKey: process.env.JWT_SECRET_KEY, - appUriScheme: process.env.APP_URI_SCHEME, + jwtSecretKey: process.env.JWT_SECRET_KEY, // optional + appUriScheme: process.env.APP_URI_SCHEME, // optional }; From 71b263715230b94c9018d105f23f92cd45a8bec2 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 1 Feb 2023 22:34:09 +0900 Subject: [PATCH 074/308] Refactor: app.js --- app.js | 12 +- package-lock.json | 338 ++++++++++++++++++++++++++++++++++++++ package.json | 2 + src/middleware/swagger.js | 39 ++--- 4 files changed, 363 insertions(+), 28 deletions(-) diff --git a/app.js b/app.js index 32930230..c9956922 100644 --- a/app.js +++ b/app.js @@ -2,9 +2,6 @@ const express = require("express"); const http = require("http"); -const cookieParser = require("cookie-parser"); -const cors = require("cors"); - // 내부 모듈 const security = require("./security"); const logger = require("./src/modules/logger"); @@ -15,12 +12,17 @@ const startSocketServer = require("./src/modules/socket"); const app = express(); app.use(express.urlencoded({ extended: false })); app.use(express.json()); -app.use(cors({ origin: true, credentials: true })); + +// CORS 설정 +app.use(require("cors")({ origin: true, credentials: true })); // 세션 및 쿠키 const session = require("./src/middleware/session"); app.use(session); -app.use(cookieParser()); +app.use(require("cookie-parser")()); + +// Swagger (API 문서) +app.use(require("./src/middleware/swagger")); // API 접근 기록 및 응답 시간을 http response의 헤더에 기록합니다. app.use(require("response-time")(logAPIAccess)); diff --git a/package-lock.json b/package-lock.json index 93a20f06..2c5f36d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,8 @@ "redis": "^4.2.0", "response-time": "^2.3.2", "socket.io": "^4.3.1", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^4.6.0", "validator": "^13.7.0", "winston": "^3.8.1", "winston-daily-rotate-file": "^4.7.1" @@ -139,6 +141,46 @@ "node": ">=6.0.0" } }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -2165,6 +2207,11 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3055,6 +3102,11 @@ "hoist-non-react-statics": "^3.3.0" } }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, "node_modules/@types/node": { "version": "18.7.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.5.tgz", @@ -3710,6 +3762,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -6327,6 +6384,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -6337,6 +6399,11 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -6367,6 +6434,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -7063,6 +7135,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-types": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz", + "integrity": "sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==", + "peer": true + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -8764,6 +8842,90 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "dependencies": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "bin": { + "swagger-jsdoc": "bin/swagger-jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/swagger-jsdoc/node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/swagger-jsdoc/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/swagger-jsdoc/node_modules/yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "dependencies": { + "@apidevtools/swagger-parser": "10.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swagger-ui-dist": { + "version": "4.15.5", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.15.5.tgz", + "integrity": "sha512-V3eIa28lwB6gg7/wfNvAbjwJYmDXy1Jo1POjyTzlB6wPcHiGlRxq39TSjYGVjQrUSAzpv+a7nzp7mDxgNy57xA==" + }, + "node_modules/swagger-ui-express": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.0.tgz", + "integrity": "sha512-ZxpQFp1JR2RF8Ar++CyJzEDdvufa08ujNUJgMVTMWPi86CuQeVdBtvaeO/ysrz6dJAYXf9kbVNhWD7JWocwqsA==", + "dependencies": { + "swagger-ui-dist": ">=4.11.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0" + } + }, "node_modules/terser": { "version": "5.15.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", @@ -9489,6 +9651,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } } }, "dependencies": { @@ -9566,6 +9756,40 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "requires": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==" + }, + "@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "requires": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + } + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -10974,6 +11198,11 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -11607,6 +11836,11 @@ "hoist-non-react-statics": "^3.3.0" } }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, "@types/node": { "version": "18.7.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.5.tgz", @@ -12116,6 +12350,11 @@ "get-intrinsic": "^1.0.2" } }, + "call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -14009,6 +14248,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -14019,6 +14263,11 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -14049,6 +14298,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -14569,6 +14823,12 @@ "mimic-fn": "^2.1.0" } }, + "openapi-types": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz", + "integrity": "sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==", + "peer": true + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -15828,6 +16088,65 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "requires": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "dependencies": { + "commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==" + } + } + }, + "swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "requires": { + "@apidevtools/swagger-parser": "10.0.3" + } + }, + "swagger-ui-dist": { + "version": "4.15.5", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.15.5.tgz", + "integrity": "sha512-V3eIa28lwB6gg7/wfNvAbjwJYmDXy1Jo1POjyTzlB6wPcHiGlRxq39TSjYGVjQrUSAzpv+a7nzp7mDxgNy57xA==" + }, + "swagger-ui-express": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.0.tgz", + "integrity": "sha512-ZxpQFp1JR2RF8Ar++CyJzEDdvufa08ujNUJgMVTMWPi86CuQeVdBtvaeO/ysrz6dJAYXf9kbVNhWD7JWocwqsA==", + "requires": { + "swagger-ui-dist": ">=4.11.0" + } + }, "terser": { "version": "5.15.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", @@ -16362,6 +16681,25 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "requires": { + "commander": "^9.4.1", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "dependencies": { + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true + } + } } } } diff --git a/package.json b/package.json index f6ecb4c8..c4aac948 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "redis": "^4.2.0", "response-time": "^2.3.2", "socket.io": "^4.3.1", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^4.6.0", "validator": "^13.7.0", "winston": "^3.8.1", "winston-daily-rotate-file": "^4.7.1" diff --git a/src/middleware/swagger.js b/src/middleware/swagger.js index 82230813..cafac3d3 100644 --- a/src/middleware/swagger.js +++ b/src/middleware/swagger.js @@ -1,27 +1,20 @@ -const express = require('express'); -const swaggerUi = require('swagger-ui-express'); -const swaggereJsdoc = require('swagger-jsdoc'); +const express = require("express"); +const swaggerUi = require("swagger-ui-express"); +const swaggereJsdoc = require("swagger-jsdoc"); -// https://github.com/Surnet/swagger-jsdoc - -const swaggerProvider = (options) => { - const router = express.Router(); - const options = { - definition: { - info: { - title: 'Test API', - version: '1.0.0', - description: 'Test API with express', - }, - basePath: '/', - ...options, +const router = express.Router(); +const options = { + definition: { + info: { + title: "Test API", + version: "1.0.0", + description: "Test API with express", }, - apis: ['./routes/*.js'] - }; - - router.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggereJsdoc(options))); + basePath: "/", + }, + apis: ["./route/*.js"], +}; - return router; -} +router.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggereJsdoc(options))); -module.exports = swaggerProvider; +module.exports = router; From e50defbcd952ba71f92e15f4ffb9da4549bcc643 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 1 Feb 2023 14:26:12 +0000 Subject: [PATCH 075/308] Test: add reports.js --- test/reports.js | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 test/reports.js diff --git a/test/reports.js b/test/reports.js new file mode 100644 index 00000000..272492d9 --- /dev/null +++ b/test/reports.js @@ -0,0 +1,54 @@ +const expect = require("chai").expect; +const reportHandlers = require("../src/service/reports"); +const { userModel } = require("../src/db/mongo"); +const { userGenerator, testRemover } = require("./utils"); +const httpMocks = require("node-mocks-http"); + +let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; +const removeTestData = async () => { + await testRemover(testData); +}; + +// 1. test1 유저가 test2 유저를 미결제로 신고, 성공 메세지가 제대로 오는지 확인 +describe("[reports] 1.createHandler", () => { + it("should return correct response from handler", async () => { + let testUser1 = await userGenerator("test1", testData); + let testUser2 = await userGenerator("test2", testData); + const msg = "User/report : report successful"; + let req = httpMocks.createRequest({ + userId: testUser1.id, + body: { + reportedId: testUser2._id, + type: "no-settlement", + etcDetail: "", + time: Date.now(), + }, + }); + let res = httpMocks.createResponse(); + await reportHandlers.createHandler(req, res); + + expect(res).to.has.property("statusCode", 200); + expect(res._getData()).to.equal(msg); + }); +}); + +// 2. test1 유저의 신고한/신고받은 내역이 제대로 오는지 확인, 신고한 내역 작성자에 test1이 있는지 확인 +describe("[reports] 2.searchByUserHandler", () => { + it("should return correct reporting/reported reports of users", async () => { + const testUser1 = await userModel.findOne({ id: "test1" }); + let req = httpMocks.createRequest({ + userId: testUser1.id, + }); + let res = httpMocks.createResponse(); + await reportHandlers.searchByUserHandler(req, res); + afterEach(removeTestData); + + expect(res).to.has.property("statusCode", 200); + expect(res._getJSONData()).to.has.property("reporting"); + expect(res._getJSONData()).to.has.property("reported"); + expect(res._getJSONData().reporting[0]).to.has.property( + "creatorId", + testUser1._id.toString() + ); + }); +}); From ee1fa21025f711d97414073ee3c1c390ddbd9d93 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 1 Feb 2023 14:31:42 +0000 Subject: [PATCH 076/308] Test: add package.json --- package-lock.json | 53 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + test/rooms.js | 8 +++---- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 93a20f06..2850840e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "express-validator": "^6.14.0", "jsonwebtoken": "^8.5.1", "mongoose": "^6.5.2", + "node-mocks-http": "^1.12.1", "querystring": "^0.2.1", "redis": "^4.2.0", "response-time": "^2.3.2", @@ -6889,6 +6890,34 @@ "node": ">= 0.6" } }, + "node_modules/node-mocks-http": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.1.tgz", + "integrity": "sha512-jrA7Sn3qI6GsHgWtUW3gMj0vO6Yz0nJjzg3jRZYjcfj4tzi8oWPauDK1qHVJoAxTbwuDHF1JiM9GISZ/ocI/ig==", + "dependencies": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/node-mocks-http/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -14446,6 +14475,30 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "node-mocks-http": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.1.tgz", + "integrity": "sha512-jrA7Sn3qI6GsHgWtUW3gMj0vO6Yz0nJjzg3jRZYjcfj4tzi8oWPauDK1qHVJoAxTbwuDHF1JiM9GISZ/ocI/ig==", + "requires": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + } + } + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", diff --git a/package.json b/package.json index f6ecb4c8..46aa8ef5 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "express-validator": "^6.14.0", "jsonwebtoken": "^8.5.1", "mongoose": "^6.5.2", + "node-mocks-http": "^1.12.1", "querystring": "^0.2.1", "redis": "^4.2.0", "response-time": "^2.3.2", diff --git a/test/rooms.js b/test/rooms.js index d97da381..38fef24c 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -6,6 +6,10 @@ const { userGenerator, testRemover } = require("./utils"); const app = express(); let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; +const removeTestData = async () => { + // drop all testData + await testRemover(testData); +}; // rooms.js 관련 8개의 handler을 테스트 // 1. test1이 1분 뒤에 출발하는 test-room 방을 생성 @@ -167,10 +171,6 @@ describe("[rooms] 7.settlementHandler", () => { // 8. test2 방에서 퇴장, 생성해준 data 모두 삭제 describe("[rooms] 8.abortHandler", () => { - const removeTestData = async () => { - // drop all testData - await testRemover(testData); - }; it("should return information of room and abort user", async () => { const testUser2 = await userModel.findOne({ id: "test2" }); const testRoom = await roomModel.findOne({ name: "test-room" }); From 62e17a5fb646551b8a081558440288a5c31c5434 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 1 Feb 2023 23:44:09 +0900 Subject: [PATCH 077/308] Docs: logininfo.js --- app.js | 6 +-- src/middleware/swagger.js | 20 +++++++-- src/route/docs/logininfo.md | 83 ------------------------------------- src/route/logininfo.js | 71 +++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 90 deletions(-) delete mode 100644 src/route/docs/logininfo.md diff --git a/app.js b/app.js index c9956922..e682ff23 100644 --- a/app.js +++ b/app.js @@ -21,12 +21,12 @@ const session = require("./src/middleware/session"); app.use(session); app.use(require("cookie-parser")()); -// Swagger (API 문서) -app.use(require("./src/middleware/swagger")); - // API 접근 기록 및 응답 시간을 http response의 헤더에 기록합니다. app.use(require("response-time")(logAPIAccess)); +// Swagger (API 문서) +app.use("/docs", require("./src/middleware/swagger")); + // admin 페이지는 rate limiting을 적용하지 않습니다. app.use("/admin/logs", require("./src/route/admin.logs")); app.use("/admin", require("./src/route/admin")); diff --git a/src/middleware/swagger.js b/src/middleware/swagger.js index cafac3d3..f4b9baee 100644 --- a/src/middleware/swagger.js +++ b/src/middleware/swagger.js @@ -5,16 +5,28 @@ const swaggereJsdoc = require("swagger-jsdoc"); const router = express.Router(); const options = { definition: { + openapi: "3.0.3", info: { - title: "Test API", + title: "Taxi API Document", version: "1.0.0", - description: "Test API with express", }, basePath: "/", + tags: [ + { + name: "logininfo", + description: "로그인 정보 제공", + }, + ], + consumes: ["application/json"], + produces: ["application/json"], }, - apis: ["./route/*.js"], + apis: ["src/route/*.js"], }; -router.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggereJsdoc(options))); +router.use( + "/", + swaggerUi.serve, + swaggerUi.setup(swaggereJsdoc(options), { explorer: true }) +); module.exports = router; diff --git a/src/route/docs/logininfo.md b/src/route/docs/logininfo.md deleted file mode 100644 index 723af183..00000000 --- a/src/route/docs/logininfo.md +++ /dev/null @@ -1,83 +0,0 @@ -## `/json/logininfo` - -- 로그인 정보 제공을 지원하는 API. - -### `/` **(GET)** - -- 사용자의 로그인 세션이 유효한 경우 사용자의 정보를 반환하는 API. - -#### URL Parameters - -- 없음 - -#### Response - -- 현재 로그인된 사용자의 정보 - -```javascript -{ - id: String, - sid: String, - name: String, -} -``` - -- 로그인되어있지 않은 경우 아래 정보를 반환함. - -```javascript -{ - id: undefined, - sid: undefined, - name: undefined, -} -``` - -#### Errors - -- 없음 - -### `/detail` **(GET)** - -- 사용자의 로그인 세션이 유효한 경우 사용자의 **상세한** 정보를 반환하는 API. - -#### URL Parameters - -- 없음 - -#### Response - -- 현재 로그인된 사용자의 정보 - -```javascript -{ - id: String, - nickname: String, - withdraw: Boolean, - ban: Boolean, - joinat: Date, - subinfo: { - kaist: String, // KAIST 학번(8자리) - sparcs: String, - facebook: String, - twitter: String, - }, -} -``` - -- 로그인되어있지 않은 경우 아래 정보를 반환함. - -```javascript -{ - id: undefined, -} -``` - -#### Errors - -- 백엔드에서 오류가 발생했을 때 - -```javascript -{ - err: true, -} -``` diff --git a/src/route/logininfo.js b/src/route/logininfo.js index 6e746682..46135f86 100644 --- a/src/route/logininfo.js +++ b/src/route/logininfo.js @@ -3,7 +3,78 @@ const express = require("express"); const router = express.Router(); const logininfoHandlers = require("../service/logininfo"); +/** + * @swagger + * /logininfo: + * get: + * tags: [logininfo] + * summary: 사용자 정보 반환 + * description: 로그인되어 있는 사용자의 정보를 반환 + * responses: + * 200: + * description: 사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
+ * 세션이 유효하지 않은 경우, 빈 오브젝트를 반환 + * content: + * application/json: + * schema: + * type: object + * properties: + * id: + * type: string + * description: 사용자 id + * sid: + * type: string + * description: 사용자 sid + * name: + * type: string + * description: 사용자 이름 + */ router.route("/").get(logininfoHandlers.logininfoHandler); + +/** + * @swagger + * /logininfo/detail: + * get: + * tags: [logininfo] + * summary: 상세한 사용자 정보 반환 + * description: 로그인되어 있는 사용자의 상세한 정보를 반환 + * responses: + * 200: + * description: 사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
+ * 세션이 유효하지 않은 경우, 빈 오브젝트를 반환 + * content: + * application/json: + * schema: + * type: object + * properties: + * id: + * type: string + * description: 사용자 id + * nickname: + * type: string + * withdraw: + * type: boolean + * ban: + * type: boolean + * joinat: + * type: string + * format: date-time + * subinfio: + * type: object + * properties: + * kaist: + * type: string + * description: KAIST 학번(8자리) + * minLength: 8 + * maxLength: 8 + * example: 20190052 + * sparcs: + * type: string + * facebook: + * type: string + * twitter: + * type: string + */ router.route("/detail").get(logininfoHandlers.detailHandler); module.exports = router; From 43c848f04144c6160d662491e368b2b0b911cbe0 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 1 Feb 2023 23:47:04 +0900 Subject: [PATCH 078/308] Docs: logininfo --- src/route/logininfo.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/route/logininfo.js b/src/route/logininfo.js index 46135f86..d221dd8c 100644 --- a/src/route/logininfo.js +++ b/src/route/logininfo.js @@ -47,9 +47,14 @@ router.route("/").get(logininfoHandlers.logininfoHandler); * schema: * type: object * properties: + * oid: + * type: string * id: * type: string * description: 사용자 id + * name: + * type: string + * description: 사용자 이름 * nickname: * type: string * withdraw: @@ -59,6 +64,8 @@ router.route("/").get(logininfoHandlers.logininfoHandler); * joinat: * type: string * format: date-time + * agreeOnTermsOfService: + * type: boolean * subinfio: * type: object * properties: @@ -74,6 +81,13 @@ router.route("/").get(logininfoHandlers.logininfoHandler); * type: string * twitter: * type: string + * email: + * type: string + * example: geon6757@kaist.ac.kr + * profileImgUrl: + * type: string + * account: + * type: string */ router.route("/detail").get(logininfoHandlers.detailHandler); From 579b7a9cfc7579e593548157003ca0412b3d1603 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Thu, 2 Feb 2023 15:29:30 +0900 Subject: [PATCH 079/308] Refactor: app.js --- app.js | 30 ++++++++++---------- src/{middleware/swagger.js => route/docs.js} | 7 ++--- 2 files changed, 17 insertions(+), 20 deletions(-) rename src/{middleware/swagger.js => route/docs.js} (80%) diff --git a/app.js b/app.js index e682ff23..271197bd 100644 --- a/app.js +++ b/app.js @@ -1,8 +1,6 @@ -// 외부 모듈 require +// 모듈 require const express = require("express"); const http = require("http"); - -// 내부 모듈 const security = require("./security"); const logger = require("./src/modules/logger"); const logAPIAccess = require("./src/modules/logAPIAccess"); @@ -10,31 +8,33 @@ const startSocketServer = require("./src/modules/socket"); // 익스프레스 서버 생성 const app = express(); + +// [Middleware] request body 파싱 app.use(express.urlencoded({ extended: false })); app.use(express.json()); -// CORS 설정 +// [Middleware] CORS 설정 app.use(require("cors")({ origin: true, credentials: true })); -// 세션 및 쿠키 +// [Middleware] 세션 및 쿠키 const session = require("./src/middleware/session"); app.use(session); app.use(require("cookie-parser")()); -// API 접근 기록 및 응답 시간을 http response의 헤더에 기록합니다. +// [Middleware] API 접근 기록 및 응답 시간을 http response의 헤더에 기록합니다. app.use(require("response-time")(logAPIAccess)); -// Swagger (API 문서) -app.use("/docs", require("./src/middleware/swagger")); - -// admin 페이지는 rate limiting을 적용하지 않습니다. +// [Router] admin 페이지는 rate limiting을 적용하지 않습니다. app.use("/admin/logs", require("./src/route/admin.logs")); app.use("/admin", require("./src/route/admin")); -// Apply the rate limiting middleware to all requests +// [Middleware] 모든 요청에 대하여 rate limiting 적용 app.use(require("./src/middleware/limitRate")); -// 라우터 및 리액트 +// [Router] Swagger (API 문서) +app.use("/docs", require("./src/route/docs")); + +// [Router] APIs // /rooms/v2에 요청을 보내는 기존 클라이언트 코드 호환성 유지 app.use("/auth", require("./src/route/auth")); app.use(["/logininfo", "/json/logininfo"], require("./src/route/logininfo")); @@ -45,9 +45,9 @@ app.use("/locations", require("./src/route/locations")); app.use("/reports", require("./src/route/reports")); // express 서버 시작 -const serverHttp = http.createServer(app).listen(security.port, () => { - logger.info(`Express 서버가 ${security.port}번 포트에서 시작됨.`); -}); +const serverHttp = http.createServer(app).listen(security.port, () => + logger.info(`Express 서버가 ${security.port}번 포트에서 시작됨.`) +); // socket.io 서버 시작 및 app 인스턴스에 저장 app.set("io", startSocketServer(serverHttp, session)); diff --git a/src/middleware/swagger.js b/src/route/docs.js similarity index 80% rename from src/middleware/swagger.js rename to src/route/docs.js index f4b9baee..e2ec2241 100644 --- a/src/middleware/swagger.js +++ b/src/route/docs.js @@ -23,10 +23,7 @@ const options = { apis: ["src/route/*.js"], }; -router.use( - "/", - swaggerUi.serve, - swaggerUi.setup(swaggereJsdoc(options), { explorer: true }) -); +router.use(swaggerUi.serve); +router.get(swaggerUi.setup(swaggereJsdoc(options), { explorer: true })); module.exports = router; From b043d7305478e4eba0430b796ad7de31a5c89a81 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Thu, 2 Feb 2023 15:44:00 +0900 Subject: [PATCH 080/308] Add: swaggerSpec --- src/route/docs.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/route/docs.js b/src/route/docs.js index e2ec2241..aa176548 100644 --- a/src/route/docs.js +++ b/src/route/docs.js @@ -3,7 +3,7 @@ const swaggerUi = require("swagger-ui-express"); const swaggereJsdoc = require("swagger-jsdoc"); const router = express.Router(); -const options = { +const swaggerSpec = swaggereJsdoc({ definition: { openapi: "3.0.3", info: { @@ -21,9 +21,9 @@ const options = { produces: ["application/json"], }, apis: ["src/route/*.js"], -}; +}); router.use(swaggerUi.serve); -router.get(swaggerUi.setup(swaggereJsdoc(options), { explorer: true })); +router.get(swaggerUi.setup(swaggerSpec, { explorer: true })); module.exports = router; From 8487aac62d16bf23208045b683317f1c0026405b Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Thu, 2 Feb 2023 06:52:33 +0000 Subject: [PATCH 081/308] Test: apply format --- test/users.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/users.js b/test/users.js index 376bca3b..c1eceb08 100644 --- a/test/users.js +++ b/test/users.js @@ -39,7 +39,7 @@ describe("[users] 2.getAgreeOnTermsOfServiceHandler", () => { }); }); -// 3. test1 유저의 nickname을 test-nickname으로 변경, 성공 메세지가 제대로 오는지 확인 +// 3. test1 유저의 nickname을 test-nickname으로 변경, 성공 메세지가 제대로 오는지 확인 describe("[users] 3.editNicknameHandler", () => { const testNickname = "test-nickname"; @@ -65,7 +65,7 @@ describe("[users] 3.editNicknameHandler", () => { }); }); -// 3. test1 유저의 계좌번호를 testAccount으로 변경, 성공 메세지가 제대로 오는지 확인 +// 3. test1 유저의 계좌번호를 testAccount으로 변경, 성공 메세지가 제대로 오는지 확인 describe("[users] 4.editAccountHandler", () => { const testAccount = "신한 0123456789012"; @@ -92,10 +92,11 @@ describe("[users] 4.editAccountHandler", () => { }); // 5. test1 유저의 프로필 업로드를 위한 PUrl을 제대로 받았는지 확인 +// 추가 검증을 위해, key와 Content-Type이 일치하는지 확인 describe("[users] 5.editProfileImgGetPUrlHandler", () => { it("should return url and fields of data", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); - const testImgType = "image/jpg" + const testImgType = "image/jpg"; let req = httpMocks.createRequest({ userId: testUser1.id, body: { @@ -107,10 +108,14 @@ describe("[users] 5.editProfileImgGetPUrlHandler", () => { expect(res).to.has.property("statusCode", 200); expect(res._getJSONData()).to.has.property("url"); - expect(res._getJSONData().fields).to.has.property("key", - `profile-img/${testUser1._id}`); - expect(res._getJSONData().fields).to.has.property("Content-Type", - testImgType); + expect(res._getJSONData().fields).to.has.property( + "key", + `profile-img/${testUser1._id}` + ); + expect(res._getJSONData().fields).to.has.property( + "Content-Type", + testImgType + ); }); }); From 64ae8a34280d6303baa9235cddb5e2359b3542eb Mon Sep 17 00:00:00 2001 From: withsang Date: Sun, 5 Feb 2023 20:41:52 +0900 Subject: [PATCH 082/308] Add: add debug codes for FCM --- .env.example | 2 +- app.js | 4 ++-- src/modules/fcm.js | 35 +++++++++++++++++++---------- src/route/auth.replace.js | 4 ++-- src/service/auth.replace.js | 45 ++++++++++++++++++++++++++++--------- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/.env.example b/.env.example index 54182910..9818a8ff 100644 --- a/.env.example +++ b/.env.example @@ -10,4 +10,4 @@ AWS_SECRET_ACCESS_KEY=[AWS Secret access key] AWS_S3_BUCKET_NAME=[AWS S3 Buck name] JWT_SECRET_KEY=[JWT SERCRET KEY] APP_URI_SCHEME=[APP_URI_SCHEME] -GOOGLE_APPLICATION_CREDENTIALS=[GOOGLE_APPLICATION_CREDENTIALS_LOCATION] \ No newline at end of file +GOOGLE_APPLICATION_CREDENTIALS=firebase-admin-sdk-account.json \ No newline at end of file diff --git a/app.js b/app.js index 6b558719..606ab680 100644 --- a/app.js +++ b/app.js @@ -13,9 +13,9 @@ const startSocketServer = require("./src/modules/socket"); // Firebase Admin 초기설정 -var admin = require("firebase-admin"); +const admin = require("firebase-admin"); -var serviceAccount = require(security.googleApplicationCredentials); +const serviceAccount = require(security.googleApplicationCredentials); admin.initializeApp({ credential: admin.credential.cert(serviceAccount), diff --git a/src/modules/fcm.js b/src/modules/fcm.js index f2b6bebc..ac53a48a 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -1,4 +1,5 @@ -var admin = require("firebase-admin"); +const { getMessaging } = require("firebase-admin/messaging"); +const axios = require("axios"); const logger = require("../modules/logger"); /** @@ -7,11 +8,22 @@ const logger = require("../modules/logger"); * @return {Promise} 토큰이 현재 유효한 경우 true, 아닌 경우 false를 반환합니다. */ const validateDeviceToken = async (deviceToken) => { + // TO DO THIS, WE FIRST NEED TO GET ACCESS TOKEN FROM THE CREDENTIAL VIA API REQUEST. + // Otherwise, you will see a "MissingAuthorization" error. try { // Check if the status code is 200. If 400 or 404, then the token is not valid. - const response = await fetch( - `https://https://iid.googleapis.com/iid/info/${deviceToken}?details=true` - ); + // I think there will be a better method...... + // const serverKey = ""; + // const response = await axios.post( + // `https://https://iid.googleapis.com/iid/info/${deviceToken}?details=true`, + // {}, + // { + // headers: { + // Authorization: `Bearer ${serverKey}`, + // }, + // } + // ); + const response = { status: 200 }; if (response.status === 200) { return true; } else { @@ -25,12 +37,10 @@ const validateDeviceToken = async (deviceToken) => { const sendNotificationMultipleUsers = async (message, tokens) => { try { const tokenMessage = { - ...message, + data: message, tokens: tokens, }; - const { failureCount } = await admin - .messaging() - .sendMulticase(tokenMessage); + const { failureCount } = await getMessaging().sendMulticast(tokenMessage); return failureCount; } catch (error) { logger.error(error); @@ -41,10 +51,11 @@ const sendNotificationMultipleUsers = async (message, tokens) => { const sendNotification = async (message, token) => { try { const tokenMessage = { - ...message, + data: message, token: token, }; - const response = await admin.messaging.send(tokenMessage); + logger.info(tokenMessage.token); + const response = await getMessaging().send(tokenMessage); logger.info(`Notification sent to token ${token}`); return true; } catch (error) { @@ -56,10 +67,10 @@ const sendNotification = async (message, token) => { const sendMessageTopic = async (message, topic) => { try { const topicMessage = { - ...message, + data: message, topic: topic, }; - const response = await admin.messaging.send(topicMessage); + const response = await getMessaging().send(topicMessage); logger.info(`Notification sent to topic ${topic}`); return true; } catch (error) { diff --git a/src/route/auth.replace.js b/src/route/auth.replace.js index 760e2d66..2f071e0d 100755 --- a/src/route/auth.replace.js +++ b/src/route/auth.replace.js @@ -3,7 +3,7 @@ const router = express.Router(); const authReplaceHandlers = require("../service/auth.replace"); const setTimestamp = require("../middleware/setTimestamp"); -const { authMiddleware } = require("../middleware/auth"); +const authMiddleware = require("../middleware/auth"); // 로그인 시도 router.route("/try").post(setTimestamp, authReplaceHandlers.tryHandler); @@ -16,7 +16,7 @@ router.route("/logout").get(authReplaceHandlers.logoutHandler); // FCM 토큰 등록 router - .route("/deviceToken") + .route("/registerDeviceToken") .post(authMiddleware, authReplaceHandlers.registerDeviceTokenHandler); module.exports = router; diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index 78e2a64d..49759ca1 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -1,6 +1,6 @@ const security = require("../../security"); const { userModel, deviceTokenModel } = require("../db/mongo"); -const { validateDeviceToken } = require("../modules/fcm"); +const { sendNotification } = require("../modules/fcm"); const { logout, login } = require("../auth/login"); const { generateNickname, @@ -132,17 +132,40 @@ const logoutHandler = (req, res) => { const registerDeviceTokenHandler = async (req, res) => { try { const deviceToken = req.body.deviceToken; - // FCM 토큰이 현재 유효한지 검사합니다. - const isTokenAlive = await validateDeviceToken(deviceToken); - if (!isTokenAlive) { - return res.status(404).send("token not found"); - } + logger.info(`\nFCM TOKEN START\n${deviceToken}\nFCM TOKEN END`); + // // FCM 토큰이 현재 유효한지 검사합니다. + // const isTokenAlive = await validateDeviceToken(deviceToken); + // if (!isTokenAlive) { + // return res.status(404).send("token not found"); + // } // 데이터베이스에 새 레코드를 추가합니다. - const newDeviceToken = new deviceTokenModel({ - userId: req.session.user_id, - deviceToken, - }); - await newDeviceToken.save(); + const user = await userModel.findOne({ id: req.userId }, "_id"); + const newDeviceToken = await deviceTokenModel.updateOne( + { + userId: user._id, + deviceToken, + }, + { upsert: true, new: true } + ); + if (req.userId !== "hello") { + const helloUser = await userModel.findOne({ id: "hello" }, "_id"); + const helloToken = await deviceTokenModel.findOne({ + userId: helloUser._id, + }); + const sendResult = await sendNotification( + { + Hello: "world", + }, + helloToken.deviceToken + ); + logger.info(sendResult); + } + await sendNotification( + { + hello: "world", + }, + deviceToken + ); return res.status(200).json({ deviceToken, }); From 35753297df82f8bc62320b20e9241adb0169f986 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 8 Feb 2023 22:07:09 +0900 Subject: [PATCH 083/308] Fix docs.js --- app.js | 8 +++++--- src/route/docs.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 271197bd..ca3650a7 100644 --- a/app.js +++ b/app.js @@ -45,9 +45,11 @@ app.use("/locations", require("./src/route/locations")); app.use("/reports", require("./src/route/reports")); // express 서버 시작 -const serverHttp = http.createServer(app).listen(security.port, () => - logger.info(`Express 서버가 ${security.port}번 포트에서 시작됨.`) -); +const serverHttp = http + .createServer(app) + .listen(security.port, () => + logger.info(`Express 서버가 ${security.port}번 포트에서 시작됨.`) + ); // socket.io 서버 시작 및 app 인스턴스에 저장 app.set("io", startSocketServer(serverHttp, session)); diff --git a/src/route/docs.js b/src/route/docs.js index aa176548..0be1acf9 100644 --- a/src/route/docs.js +++ b/src/route/docs.js @@ -24,6 +24,6 @@ const swaggerSpec = swaggereJsdoc({ }); router.use(swaggerUi.serve); -router.get(swaggerUi.setup(swaggerSpec, { explorer: true })); +router.use(swaggerUi.setup(swaggerSpec, { explorer: true })); module.exports = router; From 568f5c454beaad5ce1b2a8c1aaa1a5e2ea349322 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 8 Feb 2023 13:25:54 +0000 Subject: [PATCH 084/308] Refactor: locations.js --- package-lock.json | 53 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + test/locations.js | 11 +++++----- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 93a20f06..2850840e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "express-validator": "^6.14.0", "jsonwebtoken": "^8.5.1", "mongoose": "^6.5.2", + "node-mocks-http": "^1.12.1", "querystring": "^0.2.1", "redis": "^4.2.0", "response-time": "^2.3.2", @@ -6889,6 +6890,34 @@ "node": ">= 0.6" } }, + "node_modules/node-mocks-http": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.1.tgz", + "integrity": "sha512-jrA7Sn3qI6GsHgWtUW3gMj0vO6Yz0nJjzg3jRZYjcfj4tzi8oWPauDK1qHVJoAxTbwuDHF1JiM9GISZ/ocI/ig==", + "dependencies": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/node-mocks-http/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -14446,6 +14475,30 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "node-mocks-http": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.1.tgz", + "integrity": "sha512-jrA7Sn3qI6GsHgWtUW3gMj0vO6Yz0nJjzg3jRZYjcfj4tzi8oWPauDK1qHVJoAxTbwuDHF1JiM9GISZ/ocI/ig==", + "requires": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + } + } + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", diff --git a/package.json b/package.json index f6ecb4c8..46aa8ef5 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "express-validator": "^6.14.0", "jsonwebtoken": "^8.5.1", "mongoose": "^6.5.2", + "node-mocks-http": "^1.12.1", "querystring": "^0.2.1", "redis": "^4.2.0", "response-time": "^2.3.2", diff --git a/test/locations.js b/test/locations.js index dcb3ec33..1d9c0e24 100644 --- a/test/locations.js +++ b/test/locations.js @@ -1,14 +1,13 @@ const expect = require("chai").expect; const locationHandlers = require("../src/service/locations"); +const httpMocks = require("node-mocks-http"); describe("[locations] 1.getAllLocationsHandler", () => { it("should return information of locations correctly", async () => { - const req = {}; - const res = { - json: (data) => { - expect(data.locations).not.to.have.lengthOf(0); - }, - }; + let req = httpMocks.createRequest({}); + let res = httpMocks.createResponse(); await locationHandlers.getAllLocationsHandler(req, res); + + expect(res._getJSONData().locations).not.to.have.lengthOf(0); }); }); From fbbe89abca259eeadd703f898cbd4ececd86da96 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 8 Feb 2023 14:18:27 +0000 Subject: [PATCH 085/308] Refactor: logininfo.js --- test/logininfo.js | 83 +++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 49 deletions(-) diff --git a/test/logininfo.js b/test/logininfo.js index 6f2ab0ff..f8364101 100644 --- a/test/logininfo.js +++ b/test/logininfo.js @@ -1,24 +1,23 @@ const expect = require("chai").expect; const logininfoHandlers = require("../src/service/logininfo"); const { userModel } = require("../src/db/mongo"); +const httpMocks = require("node-mocks-http"); describe("[logininfo] 1.logininfoHandler", () => { it("should return {id: undefined, sid: undefined, name: undefined } when no user is logged in", () => { - const req = { session: {} }; - const res = { - json: (data) => { - expect(data).to.deep.equal({ - id: undefined, - sid: undefined, - name: undefined, - }); - }, - }; + let req = httpMocks.createRequest({ + session: {}, + }); + let res = httpMocks.createResponse(); logininfoHandlers.logininfoHandler(req, res); + + expect(res._getJSONData().id).to.be.undefined; + expect(res._getJSONData().sid).to.be.undefined; + expect(res._getJSONData().name).to.be.undefined; }); it("should return {id: 'hello-id', sid: 'hello-sid', 'name': 'hello-name'} when user is logged in", () => { - const req = { + let req = httpMocks.createRequest({ session: { loginInfo: { id: "hello-id", @@ -27,21 +26,17 @@ describe("[logininfo] 1.logininfoHandler", () => { time: Date.now(), }, }, - }; - const res = { - json: (data) => { - expect(data).to.deep.equal({ - id: "hello-id", - sid: "hello-sid", - name: "hello-name", - }); - }, - }; + }); + let res = httpMocks.createResponse(); logininfoHandlers.logininfoHandler(req, res); + + expect(res._getJSONData()).to.has.property("id", "hello-id"); + expect(res._getJSONData()).to.has.property("sid", "hello-sid"); + expect(res._getJSONData()).to.has.property("name", "hello-name"); }); it("should return {id: undefined, sid: undefined, name: undefined } when the session is expired", () => { - const req = { + let req = httpMocks.createRequest({ session: { loginInfo: { id: "hello-id", @@ -50,31 +45,25 @@ describe("[logininfo] 1.logininfoHandler", () => { time: new Date(Date.now() - (14 * 24 * 3600 * 1000 + 1)).getTime(), // the session should expire after 1 hour }, }, - }; - const res = { - json: (data) => { - expect(data).to.deep.equal({ - id: undefined, - sid: undefined, - name: undefined, - }); - }, - }; + }); + let res = httpMocks.createResponse(); logininfoHandlers.logininfoHandler(req, res); + + expect(res._getJSONData().id).to.be.undefined; + expect(res._getJSONData().sid).to.be.undefined; + expect(res._getJSONData().name).to.be.undefined; }); }); describe("[logininfo] 2.detailHandler", () => { it("should return { id: undefined } when no user is logged in", () => { - const req = { session: {} }; - const res = { - json: (data) => { - expect(data).to.deep.equal({ - id: undefined, - }); - }, - }; + let req = httpMocks.createRequest({ + session: {}, + }); + let res = httpMocks.createResponse(); logininfoHandlers.detailHandler(req, res); + + expect(res._getJSONData().id).to.be.undefined; }); it("should return correct information as same as user's when user is logged in", async () => { @@ -114,7 +103,7 @@ describe("[logininfo] 2.detailHandler", () => { }); it("should return {id: undefined} when the session is expired", () => { - const req = { + let req = httpMocks.createRequest({ session: { loginInfo: { id: "hello-id", @@ -123,14 +112,10 @@ describe("[logininfo] 2.detailHandler", () => { time: new Date(Date.now() - (14 * 24 * 3600 * 1000 + 1)).getTime(), // the session should expire after 1 hour }, }, - }; - const res = { - json: (data) => { - expect(data).to.deep.equal({ - id: undefined, - }); - }, - }; + }); + let res = httpMocks.createResponse(); logininfoHandlers.detailHandler(req, res); + + expect(res._getJSONData().id).to.be.undefined; }); }); From a23374c78b0021977713bcf6298b3ef5dc7e35c6 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 8 Feb 2023 14:25:58 +0000 Subject: [PATCH 086/308] Refactor: redo changes in logininfo.js --- test/logininfo.js | 83 ++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/test/logininfo.js b/test/logininfo.js index f8364101..6f2ab0ff 100644 --- a/test/logininfo.js +++ b/test/logininfo.js @@ -1,23 +1,24 @@ const expect = require("chai").expect; const logininfoHandlers = require("../src/service/logininfo"); const { userModel } = require("../src/db/mongo"); -const httpMocks = require("node-mocks-http"); describe("[logininfo] 1.logininfoHandler", () => { it("should return {id: undefined, sid: undefined, name: undefined } when no user is logged in", () => { - let req = httpMocks.createRequest({ - session: {}, - }); - let res = httpMocks.createResponse(); + const req = { session: {} }; + const res = { + json: (data) => { + expect(data).to.deep.equal({ + id: undefined, + sid: undefined, + name: undefined, + }); + }, + }; logininfoHandlers.logininfoHandler(req, res); - - expect(res._getJSONData().id).to.be.undefined; - expect(res._getJSONData().sid).to.be.undefined; - expect(res._getJSONData().name).to.be.undefined; }); it("should return {id: 'hello-id', sid: 'hello-sid', 'name': 'hello-name'} when user is logged in", () => { - let req = httpMocks.createRequest({ + const req = { session: { loginInfo: { id: "hello-id", @@ -26,17 +27,21 @@ describe("[logininfo] 1.logininfoHandler", () => { time: Date.now(), }, }, - }); - let res = httpMocks.createResponse(); + }; + const res = { + json: (data) => { + expect(data).to.deep.equal({ + id: "hello-id", + sid: "hello-sid", + name: "hello-name", + }); + }, + }; logininfoHandlers.logininfoHandler(req, res); - - expect(res._getJSONData()).to.has.property("id", "hello-id"); - expect(res._getJSONData()).to.has.property("sid", "hello-sid"); - expect(res._getJSONData()).to.has.property("name", "hello-name"); }); it("should return {id: undefined, sid: undefined, name: undefined } when the session is expired", () => { - let req = httpMocks.createRequest({ + const req = { session: { loginInfo: { id: "hello-id", @@ -45,25 +50,31 @@ describe("[logininfo] 1.logininfoHandler", () => { time: new Date(Date.now() - (14 * 24 * 3600 * 1000 + 1)).getTime(), // the session should expire after 1 hour }, }, - }); - let res = httpMocks.createResponse(); + }; + const res = { + json: (data) => { + expect(data).to.deep.equal({ + id: undefined, + sid: undefined, + name: undefined, + }); + }, + }; logininfoHandlers.logininfoHandler(req, res); - - expect(res._getJSONData().id).to.be.undefined; - expect(res._getJSONData().sid).to.be.undefined; - expect(res._getJSONData().name).to.be.undefined; }); }); describe("[logininfo] 2.detailHandler", () => { it("should return { id: undefined } when no user is logged in", () => { - let req = httpMocks.createRequest({ - session: {}, - }); - let res = httpMocks.createResponse(); + const req = { session: {} }; + const res = { + json: (data) => { + expect(data).to.deep.equal({ + id: undefined, + }); + }, + }; logininfoHandlers.detailHandler(req, res); - - expect(res._getJSONData().id).to.be.undefined; }); it("should return correct information as same as user's when user is logged in", async () => { @@ -103,7 +114,7 @@ describe("[logininfo] 2.detailHandler", () => { }); it("should return {id: undefined} when the session is expired", () => { - let req = httpMocks.createRequest({ + const req = { session: { loginInfo: { id: "hello-id", @@ -112,10 +123,14 @@ describe("[logininfo] 2.detailHandler", () => { time: new Date(Date.now() - (14 * 24 * 3600 * 1000 + 1)).getTime(), // the session should expire after 1 hour }, }, - }); - let res = httpMocks.createResponse(); + }; + const res = { + json: (data) => { + expect(data).to.deep.equal({ + id: undefined, + }); + }, + }; logininfoHandlers.detailHandler(req, res); - - expect(res._getJSONData().id).to.be.undefined; }); }); From 3b321d32d68e22ceda45598fa8fe4c3a9e9839a6 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 8 Feb 2023 14:31:45 +0000 Subject: [PATCH 087/308] Refactor: users.js --- test/users.js | 6 +++++- test/utils.js | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/users.js b/test/users.js index c1eceb08..c3bbff68 100644 --- a/test/users.js +++ b/test/users.js @@ -5,6 +5,9 @@ const { userGenerator, testRemover } = require("./utils"); const httpMocks = require("node-mocks-http"); let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; +const removeTestData = async () => { + await testRemover(testData); +}; // users.js 관련 5개의 handler을 테스트 // 1. test1 유저를 생성 후, agreeOnTermsOfServiceHandler가 제대로 msg를 send 하는지 확인 @@ -127,8 +130,9 @@ describe("[users] 6.editProfileImgDoneHandler", () => { userId: testUser1.id, }); let res = httpMocks.createResponse(); - await usersHandlers.editProfileImgDoneHandler(req, res); + afterEach(removeTestData); + expect(res).to.has.property("statusCode", 200); }); }); diff --git a/test/utils.js b/test/utils.js index 7c8fae45..f2bb6e8e 100644 --- a/test/utils.js +++ b/test/utils.js @@ -32,6 +32,8 @@ const userGenerator = async (username, testData) => { return testUser; }; +// 매 테스트가 끝나고 테스트 데이터를 초기화 해주기 위한 함수 +// 더미 데이터를 생성할 경우 이 함수를 통해 제거 const testRemover = async (testData) => { for (const roomData of testData["rooms"]) { await roomModel.deleteOne({ _id: roomData }); From d615f19495aa1537c70292b8f0ca70ab49f66fde Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 8 Feb 2023 23:33:58 +0900 Subject: [PATCH 088/308] Docs: locations --- src/route/docs.js | 4 +++ src/route/docs/locations.md | 36 -------------------------- src/route/locations.js | 51 ++++++++++++++++++++++++++++++++----- 3 files changed, 49 insertions(+), 42 deletions(-) delete mode 100644 src/route/docs/locations.md diff --git a/src/route/docs.js b/src/route/docs.js index 0be1acf9..3e7ccb51 100644 --- a/src/route/docs.js +++ b/src/route/docs.js @@ -12,6 +12,10 @@ const swaggerSpec = swaggereJsdoc({ }, basePath: "/", tags: [ + { + name: "locations", + description: "출발지/도착지 정보 제공", + }, { name: "logininfo", description: "로그인 정보 제공", diff --git a/src/route/docs/locations.md b/src/route/docs/locations.md deleted file mode 100644 index 6b51f0cd..00000000 --- a/src/route/docs/locations.md +++ /dev/null @@ -1,36 +0,0 @@ -## `/locations` - -- 출발지/도착지로 사용 가능한 장소 목록 조회 기능을 지원하는 API. -- 로그인된 상태에서만 접근 가능 -- Location의 type은 아래와 같다. - -```javascript -Location: { - _id: String, // ObjectID - koName: String, // 장소의 한국어 명칭 (e.g.) "택시승강장") - enName: String, // 장소의 영어 명칭 (e.g.) "Taxi Stand") -} -``` - -### `/` **(GET)** - -출발지/도착지로 사용 가능한 장소 목록 및 요청 처리 당시 서버 시각을 제공하는 API. - -#### URL parameters - -- 없음. - -#### Response - -- 서버에 저장된 location이 없을 경우에는 locations에 빈 배열을 반환함 - -```javascript -{ - locations: [Location], // 출발지/도착지로 이용 가능한 장소 목록 - time: String(ISO 8601), // ex) '2022-01-12T13:58:20.180Z' -} -``` - -#### Errors - -- 500 "internal server error" \ No newline at end of file diff --git a/src/route/locations.js b/src/route/locations.js index 045c32ee..9e56d29b 100644 --- a/src/route/locations.js +++ b/src/route/locations.js @@ -1,13 +1,52 @@ const express = require("express"); -const router = express.Router(); -const { getAllLocationsHandler } = require("../service/locations"); +const router = express.Router(); +const locationsHandlers = require("../service/locations"); -// 로그인된 상태에서만 접근 가능 +// 라우터 접근 시 로그인 필요 router.use(require("../middleware/auth")); -// return all locations on GET / -// removes objectID from the result -router.get("/", getAllLocationsHandler); +/** + * @swagger + * /locations: + * get: + * tags: [locations] + * summary: 출발지/도착지 정보 반환 + * description: 출발지/도착지로 사용 가능한 장소 목록 조회 및 요청 처리 당시 서버 시각 반환
+ * (로그인된 상태에서만 접근 가능) + * responses: + * 200: + * description: 서버에 저장된 location이 없을 경우, locations은 빈 배열 + * content: + * application/json: + * schema: + * type: object + * properties: + * locations: + * type: array + * description: 출발지/도착지로 사용 가능한 장소 목록 + * items: + * type: object + * properties: + * priority: + * type: number + * isValid: + * type: boolean + * _id: + * type: string + * koName: + * type: string + * description: 장소의 한국어 명칭 + * example: 택시승강장 + * enName: + * type: string + * description: 장소의 영어 명칭 + * example: Taxi Stand + * serverTime: + * type: string + * format: date-time + * description: 요청 처리 당시 서버 시각 + */ +router.get("/", locationsHandlers.getAllLocationsHandler); module.exports = router; From 5ed8ae360424d35b096a107d01b785495c093bae Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 12 Feb 2023 16:14:01 +0000 Subject: [PATCH 089/308] Refactor: rooms.js searchHandler --- test/rooms.js | 66 ++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index 84b4af49..c83daf72 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -4,6 +4,7 @@ const roomsHandlers = require("../src/service/rooms"); const { userModel, roomModel, locationModel } = require("../src/db/mongo"); const { userGenerator, testRemover } = require("./utils"); const app = express(); +const httpMocks = require("node-mocks-http"); let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; @@ -14,7 +15,7 @@ describe("[rooms] 1.createHandler", () => { const testUser1 = await userGenerator("test1", testData); const testFrom = await locationModel.findOne({ koName: "대전역" }); const testTo = await locationModel.findOne({ koName: "택시승강장" }); - const req = { + let req = httpMocks.createRequest({ body: { name: "test-room", from: testFrom._id, @@ -24,14 +25,13 @@ describe("[rooms] 1.createHandler", () => { }, userId: testUser1.id, app, - }; - const res = { - send: (data) => { - expect(data).to.has.property("name", "test-room"); - }, - }; - + }); + let res = httpMocks.createResponse(); await roomsHandlers.createHandler(req, res); + + const testRoom = await roomModel.findOne({ name: "test-room" }); + testData["rooms"].push(testRoom); + expect(res._getData()).to.has.property("name", "test-room"); }); }); @@ -39,19 +39,16 @@ describe("[rooms] 1.createHandler", () => { describe("[rooms] 2.infoHandler", () => { it("should return information of room", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); - const room = await roomModel.findOne({ name: "test-room" }); - const req = { - query: { id: room._id }, + const testRoom = await roomModel.findOne({ name: "test-room" }); + let req = httpMocks.createRequest({ + query: { id: testRoom._id }, userId: testUser1.id, - }; - const res = { - send: (data) => { - expect(data).to.has.property("name", "test-room"); - expect(data).to.has.property("isOver"); - }, - }; - + }); + let res = httpMocks.createResponse(); await roomsHandlers.infoHandler(req, res); + + expect(res._getData()).to.has.property("name", "test-room"); + expect(res._getData()).to.has.property("isOver"); }); }); @@ -60,22 +57,19 @@ describe("[rooms] 3.joinHandler", () => { it("should return information of room and join", async () => { const testUser2 = await userGenerator("test2", testData); const testRoom = await roomModel.findOne({ name: "test-room" }); - testData["rooms"].push(testRoom); - const req = { + let req = httpMocks.createRequest({ body: { roomId: testRoom._id, }, userId: testUser2.id, app, - }; - const res = { - send: (data) => { - expect(data).to.has.property("name", "test-room"); - expect(data.part).to.have.lengthOf(2); - }, - }; + }); + let res = httpMocks.createResponse(); await roomsHandlers.joinHandler(req, res); + + expect(res._getData()).to.has.property("name", "test-room"); + expect(res._getData().part).to.have.lengthOf(2); }); }); @@ -84,7 +78,7 @@ describe("[rooms] 4.searchHandler", () => { it("should return information of searching room", async () => { const testFrom = await locationModel.findOne({ koName: "대전역" }); const testTo = await locationModel.findOne({ koName: "택시승강장" }); - const req = { + let req = httpMocks.createRequest({ query: { name: "test-room", from: testFrom._id, @@ -93,15 +87,13 @@ describe("[rooms] 4.searchHandler", () => { withTime: true, maxPartLength: 4, }, - }; - const res = { - json: (data) => { - expect(data[0]).to.has.property("name", "test-room"); - expect(data[0]).to.has.property("settlementTotal"); - }, - }; - + }); + let res = httpMocks.createResponse(); await roomsHandlers.searchHandler(req, res); + + const resJson = res._getJSONData(); + expect(resJson[0]).to.has.property("name", "test-room"); + expect(resJson[0].settlementTotal).to.be.undefined; }); }); From 39b0077fd06f1cef5b50e910f121fc60994cd345 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 12 Feb 2023 16:15:19 +0000 Subject: [PATCH 090/308] Fix: let to const --- test/reports.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/reports.js b/test/reports.js index 272492d9..08c19ccf 100644 --- a/test/reports.js +++ b/test/reports.js @@ -12,8 +12,8 @@ const removeTestData = async () => { // 1. test1 유저가 test2 유저를 미결제로 신고, 성공 메세지가 제대로 오는지 확인 describe("[reports] 1.createHandler", () => { it("should return correct response from handler", async () => { - let testUser1 = await userGenerator("test1", testData); - let testUser2 = await userGenerator("test2", testData); + const testUser1 = await userGenerator("test1", testData); + const testUser2 = await userGenerator("test2", testData); const msg = "User/report : report successful"; let req = httpMocks.createRequest({ userId: testUser1.id, From ffa84f8aa39046084a6d62f8c5a24119d00f7574 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Sun, 12 Feb 2023 16:16:41 +0000 Subject: [PATCH 091/308] Fix: let to const --- test/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/users.js b/test/users.js index c1eceb08..d79a97ff 100644 --- a/test/users.js +++ b/test/users.js @@ -10,7 +10,7 @@ let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; // 1. test1 유저를 생성 후, agreeOnTermsOfServiceHandler가 제대로 msg를 send 하는지 확인 describe("[users] 1.agreeOnTermsOfServiceHandler", () => { it("should return correct response from handler", async () => { - let testUser1 = await userGenerator("test1", testData); + const testUser1 = await userGenerator("test1", testData); const msg = "User/agreeOnTermsOfService : agree on Terms of Service successful"; let req = httpMocks.createRequest({ From 39e2de0070a97eea29ec09d997ebc023f092b0df Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 14 Feb 2023 21:27:08 +0900 Subject: [PATCH 092/308] Add: implement sending test notification --- src/modules/fcm.js | 60 +++++++++---------------------------- src/route/auth.js | 18 +++++++++-- src/route/auth.replace.js | 12 ++++++-- src/service/auth.js | 28 +++++++++++++++-- src/service/auth.replace.js | 35 ++++------------------ 5 files changed, 70 insertions(+), 83 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index ac53a48a..350cfc9c 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -1,44 +1,11 @@ const { getMessaging } = require("firebase-admin/messaging"); -const axios = require("axios"); const logger = require("../modules/logger"); -/** - * FCM 토큰이 현재 유효한지 IID 엔드포인트에 요청을 보냄으로써 검증하는 함수입니다. - * @param {string} deviceToken - 검증할 FCM 디바이스 토큰입니다. - * @return {Promise} 토큰이 현재 유효한 경우 true, 아닌 경우 false를 반환합니다. - */ -const validateDeviceToken = async (deviceToken) => { - // TO DO THIS, WE FIRST NEED TO GET ACCESS TOKEN FROM THE CREDENTIAL VIA API REQUEST. - // Otherwise, you will see a "MissingAuthorization" error. - try { - // Check if the status code is 200. If 400 or 404, then the token is not valid. - // I think there will be a better method...... - // const serverKey = ""; - // const response = await axios.post( - // `https://https://iid.googleapis.com/iid/info/${deviceToken}?details=true`, - // {}, - // { - // headers: { - // Authorization: `Bearer ${serverKey}`, - // }, - // } - // ); - const response = { status: 200 }; - if (response.status === 200) { - return true; - } else { - return false; - } - } catch (error) { - return false; - } -}; - -const sendNotificationMultipleUsers = async (message, tokens) => { +const sendNotificationMultipleUsers = async (data, tokens) => { try { const tokenMessage = { - data: message, - tokens: tokens, + data, + tokens, }; const { failureCount } = await getMessaging().sendMulticast(tokenMessage); return failureCount; @@ -48,14 +15,14 @@ const sendNotificationMultipleUsers = async (message, tokens) => { } }; -const sendNotification = async (message, token) => { +const sendNotification = async (data, token) => { try { const tokenMessage = { - data: message, - token: token, + data, + token, }; - logger.info(tokenMessage.token); - const response = await getMessaging().send(tokenMessage); + console.log(tokenMessage); + await getMessaging().send(tokenMessage); logger.info(`Notification sent to token ${token}`); return true; } catch (error) { @@ -64,13 +31,13 @@ const sendNotification = async (message, token) => { } }; -const sendMessageTopic = async (message, topic) => { +const sendMessageTopic = async (data, topic) => { try { const topicMessage = { - data: message, - topic: topic, + data, + topic, }; - const response = await getMessaging().send(topicMessage); + await getMessaging().send(topicMessage); logger.info(`Notification sent to topic ${topic}`); return true; } catch (error) { @@ -79,8 +46,9 @@ const sendMessageTopic = async (message, topic) => { } }; +// We need more helper functions, right? + module.exports = { - validateDeviceToken, sendNotificationMultipleUsers, sendNotification, sendMessageTopic, diff --git a/src/route/auth.js b/src/route/auth.js index fc83f8b3..dc3db2da 100644 --- a/src/route/auth.js +++ b/src/route/auth.js @@ -1,12 +1,16 @@ const express = require("express"); -const security = require("../../security"); -const authReplace = require("./auth.replace"); - const router = express.Router(); +const { body } = require("express-validator"); + +const authMiddleware = require("../middleware/auth"); const setTimestamp = require("../middleware/setTimestamp"); +const validator = require("../middleware/validator"); const authHandlers = require("../service/auth"); const mobileAuthHandlers = require("../service/auth.mobile"); +const security = require("../../security"); +const authReplace = require("./auth.replace"); + router.route("/sparcssso").get(authHandlers.sparcsssoHandler); router .route("/sparcssso/callback") @@ -18,6 +22,14 @@ router.route("/app/token/refresh").get(mobileAuthHandlers.refreshAccessToken); router.route("/app/device").post(mobileAuthHandlers.registerDeviceTokenHandler); router.route("/app/device").delete(mobileAuthHandlers.removeDeviceTokenHandler); router.route("/app/token/generate").get(authHandlers.generateTokenHandler); +// FCM 토큰 등록 +router.post( + "/registerDeviceToken", + authMiddleware, + [body("deviceToken").isString().isLength({ min: 1, max: 1024 })], + validator, + authHandlers.registerDeviceTokenHandler +); // 환경변수 SPARCSSSO_CLIENT_ID 유무에 따라 로그인 방식이 변경됩니다. module.exports = security.sparcssso?.id ? router : authReplace; diff --git a/src/route/auth.replace.js b/src/route/auth.replace.js index 2f071e0d..da47d8d7 100755 --- a/src/route/auth.replace.js +++ b/src/route/auth.replace.js @@ -1,9 +1,11 @@ const express = require("express"); const router = express.Router(); +const { body } = require("express-validator"); const authReplaceHandlers = require("../service/auth.replace"); const setTimestamp = require("../middleware/setTimestamp"); const authMiddleware = require("../middleware/auth"); +const validator = require("../middleware/validator"); // 로그인 시도 router.route("/try").post(setTimestamp, authReplaceHandlers.tryHandler); @@ -15,8 +17,12 @@ router.route("/sparcssso").get(authReplaceHandlers.sparcsssoHandler); router.route("/logout").get(authReplaceHandlers.logoutHandler); // FCM 토큰 등록 -router - .route("/registerDeviceToken") - .post(authMiddleware, authReplaceHandlers.registerDeviceTokenHandler); +router.post( + "/registerDeviceToken", + authMiddleware, + [body("deviceToken").isString().isLength({ min: 1, max: 1024 })], + validator, + authReplaceHandlers.registerDeviceTokenHandler +); module.exports = router; diff --git a/src/service/auth.js b/src/service/auth.js index 5fdf0283..045a34c7 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -1,5 +1,5 @@ const security = require("../../security"); -const { userModel } = require("../db/mongo"); +const { userModel, deviceTokenModel } = require("../db/mongo"); const { getLoginInfo, logout, login } = require("../auth/login"); const { generateNickname, @@ -93,7 +93,6 @@ const generateTokenHandler = (req, res) => { }; const sparcsssoHandler = (req, res) => { - const userInfo = getLoginInfo(req); const { url, state } = client.getLoginParams(); req.session.state = state; res.redirect(url); @@ -170,9 +169,34 @@ const logoutHandler = (req, res) => { } }; +const registerDeviceTokenHandler = async (req, res) => { + try { + const newDeviceToken = req.body.deviceToken; + // 데이터베이스에 새 레코드를 추가합니다. + const user = await userModel.findOne({ id: req.userId }, "_id"); + const deviceToken = await deviceTokenModel.updateOne( + { + userId: user._id, + }, + { + userId: user._id, + $addToSet: { deviceToken: newDeviceToken }, + }, + { upsert: true, new: true } + ); + return res.status(200).json({ + deviceToken: deviceToken.deviceToken, + }); + } catch (e) { + logger.error(e); + res.status(500).send("internal server error"); + } +}; + module.exports = { sparcsssoHandler, sparcsssoCallbackHandler, logoutHandler, generateTokenHandler, + registerDeviceTokenHandler, }; diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index c0e227ee..a70197c7 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -1,6 +1,5 @@ const security = require("../../security"); const { userModel, deviceTokenModel } = require("../db/mongo"); -const { sendNotification } = require("../modules/fcm"); const { logout, login } = require("../auth/login"); const { generateNickname, @@ -136,43 +135,21 @@ const logoutHandler = (req, res) => { const registerDeviceTokenHandler = async (req, res) => { try { - const deviceToken = req.body.deviceToken; - logger.info(`\nFCM TOKEN START\n${deviceToken}\nFCM TOKEN END`); - // // FCM 토큰이 현재 유효한지 검사합니다. - // const isTokenAlive = await validateDeviceToken(deviceToken); - // if (!isTokenAlive) { - // return res.status(404).send("token not found"); - // } + const newDeviceToken = req.body.deviceToken; // 데이터베이스에 새 레코드를 추가합니다. const user = await userModel.findOne({ id: req.userId }, "_id"); - const newDeviceToken = await deviceTokenModel.updateOne( + const deviceToken = await deviceTokenModel.updateOne( { userId: user._id, - deviceToken, }, - { upsert: true, new: true } - ); - if (req.userId !== "hello") { - const helloUser = await userModel.findOne({ id: "hello" }, "_id"); - const helloToken = await deviceTokenModel.findOne({ - userId: helloUser._id, - }); - const sendResult = await sendNotification( - { - Hello: "world", - }, - helloToken.deviceToken - ); - logger.info(sendResult); - } - await sendNotification( { - hello: "world", + userId: user._id, + $addToSet: { deviceToken: newDeviceToken }, }, - deviceToken + { upsert: true, new: true } ); return res.status(200).json({ - deviceToken, + deviceToken: deviceToken.deviceToken, }); } catch (e) { logger.error(e); From 8383a6a286344fbab4fd51990e2006daa3d893bf Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 14 Feb 2023 21:31:54 +0900 Subject: [PATCH 093/308] Fix: fix conflicting versions of dependencies --- package-lock.json | 391 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) diff --git a/package-lock.json b/package-lock.json index dc545d31..508f2f09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,10 +30,13 @@ "firebase-admin": "^11.4.1", "jsonwebtoken": "^8.5.1", "mongoose": "^6.5.2", + "node-mocks-http": "^1.12.1", "querystring": "^0.2.1", "redis": "^4.2.0", "response-time": "^2.3.2", "socket.io": "^4.3.1", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^4.6.0", "validator": "^13.7.0", "winston": "^3.8.1", "winston-daily-rotate-file": "^4.7.1" @@ -140,6 +143,46 @@ "node": ">=6.0.0" } }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -2375,6 +2418,11 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3384,6 +3432,11 @@ "hoist-non-react-statics": "^3.3.0" } }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, "node_modules/@types/jsonwebtoken": { "version": "8.5.9", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", @@ -4156,6 +4209,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -7338,6 +7396,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -7348,6 +7411,11 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -7378,6 +7446,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -8043,6 +8116,34 @@ "node": ">= 6.13.0" } }, + "node_modules/node-mocks-http": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.1.tgz", + "integrity": "sha512-jrA7Sn3qI6GsHgWtUW3gMj0vO6Yz0nJjzg3jRZYjcfj4tzi8oWPauDK1qHVJoAxTbwuDHF1JiM9GISZ/ocI/ig==", + "dependencies": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/node-mocks-http/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -8217,6 +8318,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-types": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz", + "integrity": "sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==", + "peer": true + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -10170,6 +10277,90 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "dependencies": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "bin": { + "swagger-jsdoc": "bin/swagger-jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/swagger-jsdoc/node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/swagger-jsdoc/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/swagger-jsdoc/node_modules/yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "dependencies": { + "@apidevtools/swagger-parser": "10.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swagger-ui-dist": { + "version": "4.15.5", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.15.5.tgz", + "integrity": "sha512-V3eIa28lwB6gg7/wfNvAbjwJYmDXy1Jo1POjyTzlB6wPcHiGlRxq39TSjYGVjQrUSAzpv+a7nzp7mDxgNy57xA==" + }, + "node_modules/swagger-ui-express": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.0.tgz", + "integrity": "sha512-ZxpQFp1JR2RF8Ar++CyJzEDdvufa08ujNUJgMVTMWPi86CuQeVdBtvaeO/ysrz6dJAYXf9kbVNhWD7JWocwqsA==", + "dependencies": { + "swagger-ui-dist": ">=4.11.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0" + } + }, "node_modules/taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", @@ -10993,6 +11184,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } } }, "dependencies": { @@ -11070,6 +11289,40 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "requires": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==" + }, + "@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "requires": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + } + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -12655,6 +12908,11 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -13401,6 +13659,11 @@ "hoist-non-react-statics": "^3.3.0" } }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, "@types/jsonwebtoken": { "version": "8.5.9", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", @@ -14015,6 +14278,11 @@ "get-intrinsic": "^1.0.2" } }, + "call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -16365,6 +16633,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -16375,6 +16648,11 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -16405,6 +16683,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -16914,6 +17197,30 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" }, + "node-mocks-http": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.1.tgz", + "integrity": "sha512-jrA7Sn3qI6GsHgWtUW3gMj0vO6Yz0nJjzg3jRZYjcfj4tzi8oWPauDK1qHVJoAxTbwuDHF1JiM9GISZ/ocI/ig==", + "requires": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + } + } + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -17037,6 +17344,12 @@ "mimic-fn": "^2.1.0" } }, + "openapi-types": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz", + "integrity": "sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==", + "peer": true + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -18493,6 +18806,65 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "requires": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "dependencies": { + "commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==" + } + } + }, + "swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "requires": { + "@apidevtools/swagger-parser": "10.0.3" + } + }, + "swagger-ui-dist": { + "version": "4.15.5", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.15.5.tgz", + "integrity": "sha512-V3eIa28lwB6gg7/wfNvAbjwJYmDXy1Jo1POjyTzlB6wPcHiGlRxq39TSjYGVjQrUSAzpv+a7nzp7mDxgNy57xA==" + }, + "swagger-ui-express": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.0.tgz", + "integrity": "sha512-ZxpQFp1JR2RF8Ar++CyJzEDdvufa08ujNUJgMVTMWPi86CuQeVdBtvaeO/ysrz6dJAYXf9kbVNhWD7JWocwqsA==", + "requires": { + "swagger-ui-dist": ">=4.11.0" + } + }, "taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", @@ -19106,6 +19478,25 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "requires": { + "commander": "^9.4.1", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "dependencies": { + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true + } + } } } } From e29ee36d3f9f24e4db9a34dc544c68ef56c9b32b Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 14 Feb 2023 22:44:19 +0900 Subject: [PATCH 094/308] Add: implement chat message --- .env.example | 1 + security.js | 1 + src/db/awsS3.js | 5 ++ src/modules/fcm.js | 117 +++++++++++++++++++++++++++++++------- src/route/chats.socket.js | 23 +++++++- src/service/rooms.js | 14 +++++ 6 files changed, 138 insertions(+), 23 deletions(-) diff --git a/.env.example b/.env.example index 9818a8ff..21529016 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,7 @@ FRONT_URL=[front url(e.g. http://localhost:3000)] AWS_ACCESS_KEY_ID=[AWS Access key ID] AWS_SECRET_ACCESS_KEY=[AWS Secret access key] AWS_S3_BUCKET_NAME=[AWS S3 Buck name] +AWS_S3_URL=[AWS S3 url(e.g. https://.s3..amazonaws.com)] JWT_SECRET_KEY=[JWT SERCRET KEY] APP_URI_SCHEME=[APP_URI_SCHEME] GOOGLE_APPLICATION_CREDENTIALS=firebase-admin-sdk-account.json \ No newline at end of file diff --git a/security.js b/security.js index 15a409df..147b8e8b 100644 --- a/security.js +++ b/security.js @@ -16,6 +16,7 @@ module.exports = { accessKeyId: process.env.AWS_ACCESS_KEY_ID, // required secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, // required s3BucketName: process.env.AWS_S3_BUCKET_NAME, // required + s3Url: process.env.AWS_S3_URL || "", // optional }, jwtSecretKey: process.env.JWT_SECRET_KEY, appUriScheme: process.env.APP_URI_SCHEME, diff --git a/src/db/awsS3.js b/src/db/awsS3.js index fff28ba3..9fb05bc1 100644 --- a/src/db/awsS3.js +++ b/src/db/awsS3.js @@ -75,3 +75,8 @@ module.exports.foundObject = (filePath, cb) => { } ); }; + +// function to return full URL of the object +module.exports.getS3Url = (filePath) => { + return `${security.aws.s3Url}${filePath}`; +}; diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 350cfc9c..c2623eb9 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -1,13 +1,29 @@ const { getMessaging } = require("firebase-admin/messaging"); +const { deviceTokenModel } = require("../db/mongo"); const logger = require("../modules/logger"); -const sendNotificationMultipleUsers = async (data, tokens) => { +const sendNotificationByToken = async (data, token) => { try { - const tokenMessage = { + const message = { + data, + token, + }; + await getMessaging().send(message); + logger.info(`Notification sent to token ${token}`); + return true; + } catch (error) { + logger.error(error); + return false; + } +}; + +const sendNotificationByTokens = async (data, tokens) => { + try { + const message = { data, tokens, }; - const { failureCount } = await getMessaging().sendMulticast(tokenMessage); + const { failureCount } = await getMessaging().sendMulticast(message); return failureCount; } catch (error) { logger.error(error); @@ -15,15 +31,14 @@ const sendNotificationMultipleUsers = async (data, tokens) => { } }; -const sendNotification = async (data, token) => { +const sendNotificationByTopic = async (data, topic) => { try { - const tokenMessage = { + const message = { data, - token, + topic, }; - console.log(tokenMessage); - await getMessaging().send(tokenMessage); - logger.info(`Notification sent to token ${token}`); + await getMessaging().send(message); + logger.info(`Notification sent to topic ${topic}`); return true; } catch (error) { logger.error(error); @@ -31,14 +46,63 @@ const sendNotification = async (data, token) => { } }; -const sendMessageTopic = async (data, topic) => { +/** + * 주어진 token에 메시지 알림을 전송합니다. + * @param {string} token - 알림을 받을 기기의 deviceToken입니다. + * @param {string} title - 보낼 메시지의 제목입니다. + * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @return {Promise} 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. + */ +const sendMessageByToken = async (token, title, body, icon) => { + const data = { title, body, icon }; + return await sendNotificationByToken(data, token); +}; + +/** + * 주어진 token들에 메시지 알림을 전송합니다. + * @param {string} tokens - 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. + * @param {string} title - 보낼 메시지의 제목입니다. + * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @return {Promise} 알림 전송에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. + */ +const sendMessageByTokens = async (tokens, title, body, icon) => { + const data = { title, body, icon }; + return await sendNotificationByTokens(data, tokens); +}; + +/** + * 주어진 사용자를 특정한 topic에 구독시킵니다. + * @param {string} userId - topic을 구독할 사용자의 ObjectId입니다. + * @param {string} topic - 구독할 topic입니다. + * @return {Promise} 토픽 구독에 성공했으면 true, 아니면 false를 반환합니다. + */ +const subscribeUserToTopic = async (userId, topic) => { try { - const topicMessage = { - data, - topic, - }; - await getMessaging().send(topicMessage); - logger.info(`Notification sent to topic ${topic}`); + const deviceToken = await deviceTokenModel.findOne({ + userId, + }); + await getMessaging().subscribeToTopic(deviceToken.deviceToken, topic); + return true; + } catch (error) { + logger.error(error); + return false; + } +}; + +/** + * 주어진 사용자를 특정한 topic으로부터 구독 해제시킵니다. + * @param {string} userId - topic을 구독 해제할 사용자의 id입니다. + * @param {string} topic - 구독을 해제할 topic입니다. + * @return {Promise} 토픽 구독 해제에 성공했으면 true, 아니면 false를 반환합니다. + */ +const unsubscribeUserFromTopic = async (userId, topic) => { + try { + const deviceToken = await deviceTokenModel.findOne({ + userId, + }); + await getMessaging().unsubscribeFromTopic(deviceToken.deviceToken, topic); return true; } catch (error) { logger.error(error); @@ -46,10 +110,23 @@ const sendMessageTopic = async (data, topic) => { } }; -// We need more helper functions, right? +/** + * 주어진 topic을 구독하고 있는 모든 기기에 메시지 알림을 전송합니다. + * @param {string} topic - 알림을 보낼 기기들이 구독하고 있는 topic입니다. + * @param {string} title - 보낼 메시지의 제목입니다. + * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @return {Promise} 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. + */ +const sendMessageByTopic = async (topic, title, body, icon) => { + const data = { title, body, icon }; + return await sendNotificationByTopic(data, topic); +}; module.exports = { - sendNotificationMultipleUsers, - sendNotification, - sendMessageTopic, + sendMessageByToken, + sendMessageByTokens, + subscribeUserToTopic, + unsubscribeUserFromTopic, + sendMessageByTopic, }; diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 67337745..c1d14527 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -1,6 +1,8 @@ const { getLoginInfo, joinChatRoom, leaveChatRoom } = require("../auth/login"); const { roomModel, userModel, chatModel } = require("../db/mongo"); +const { getS3Url } = require("../db/awsS3"); const validator = require("validator"); +const { sendMessageByTopic, sendMessageByTokens } = require("../modules/fcm"); const logger = require("../modules/logger"); class IllegalArgumentsException { @@ -94,6 +96,7 @@ const transformChatsForRoom = async (chats) => { const ioListeners = (io, socket) => { const session = socket.handshake.session; + // 사용자가 Socket.io 서버와 연결될 때마다 발생하는 이벤트 socket.on("chats-join", async (roomId) => { try { const myUserId = getLoginInfo({ session: session }).id || ""; @@ -135,6 +138,7 @@ const ioListeners = (io, socket) => { } }); + // 사용자와 Socket.io 서버의 연결이 끊어졌을 때 발생하는 이벤트 socket.on("chats-disconnect", async () => { try { const myUserId = getLoginInfo({ session: session }).id || ""; @@ -159,10 +163,14 @@ const ioListeners = (io, socket) => { } }); + // 사용자가 채팅 메시지를 전송했을 때 발생하는 이벤트 socket.on("chats-send", async (chatMessage) => { try { const myUserId = getLoginInfo({ session: session }).id || ""; - const myUser = await userModel.findOne({ id: myUserId }, "id nickname"); + const myUser = await userModel.findOne( + { id: myUserId }, + "id nickname profileImageUrl" + ); if (!myUser) return io.to(socket.id).emit("chats-send", { err: "user not exist" }); const roomId = session.chatRoomId; @@ -170,19 +178,28 @@ const ioListeners = (io, socket) => { return io .to(socket.id) .emit("chats-send", { err: "user not join chat room" }); - - emitChatEvent(io, roomId, { + await emitChatEvent(io, roomId, { type: "text", content: chatMessage.content, authorId: myUser._id, }); io.to(socket.id).emit("chats-send", { done: true }); + + // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. + const myRoom = await roomModel.findById(roomId, "name"); + await sendMessageByTopic( + `room-${roomId}`, + myRoom.name, + chatMessage.content, + getS3Url(`/profile-img/${myUser.profileImageUrl}`) + ); } catch (err) { logger.error(err); io.to(socket.id).emit("chats-send", { err: true }); } }); + // 사용자가 과거 채팅 메시지를 로드하려 할 때 발생하는 이벤트 socket.on("chats-load", async (lastDate, amount) => { try { const roomId = session.chatRoomId; diff --git a/src/service/rooms.js b/src/service/rooms.js index c1fce152..bc801fb6 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -1,6 +1,10 @@ const { roomModel, locationModel, userModel } = require("../db/mongo"); const { emitChatEvent } = require("../route/chats.socket"); const { leaveChatRoom } = require("../auth/login"); +const { + subscribeUserToTopic, + unsubscribeUserFromTopic, +} = require("../modules/fcm"); const logger = require("../modules/logger"); const { roomPopulateOption, @@ -76,6 +80,9 @@ const createHandler = async (req, res) => { authorId: user._id, }); + // 방을 추가한 사용자를 `room-${room._id}` topic에 구독시킵니다. + logger.info(await subscribeUserToTopic(user._id, `room-${room._id}`)); + const roomObject = (await room.populate(roomPopulateOption)).toObject(); return res.send(formatSettlement(roomObject)); } catch (err) { @@ -170,6 +177,9 @@ const joinHandler = async (req, res) => { authorId: user._id, }); + // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. + logger.info(await subscribeUserToTopic(user._id, `room-${room._id}`)); + const roomObject = (await room.populate(roomPopulateOption)).toObject(); res.send(formatSettlement(roomObject)); } catch (err) { @@ -261,6 +271,10 @@ const abortHandler = async (req, res) => { content: user.id, authorId: user._id, }); + + // 방에서 나간 사용자를 `room-${room._id}` topic으로부터 구독 해제시킵니다. + logger.info(await unsubscribeUserFromTopic(user._id, `room-${room._id}`)); + const roomObject = (await room.populate(roomPopulateOption)).toObject(); const isOver = getIsOver(roomObject, user.id); From dadea9f0b0edc6c05c2a5f815815b430e83d5723 Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 14 Feb 2023 23:01:11 +0900 Subject: [PATCH 095/308] Fix: update submodule version --- sampleGenerator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sampleGenerator b/sampleGenerator index 374f7b14..a8bdc640 160000 --- a/sampleGenerator +++ b/sampleGenerator @@ -1 +1 @@ -Subproject commit 374f7b14c462e117aa2ed2ae3a718abae39ae527 +Subproject commit a8bdc640346de4bd26594a36bd4a442c04abdf2b From adb9aa1baef8869b21963f834edb68b95a428706 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 15 Feb 2023 01:19:24 +0900 Subject: [PATCH 096/308] Add: add notification click link --- src/modules/fcm.js | 40 +++++++++++++++++++++++++-------------- src/route/chats.socket.js | 11 +++++++---- src/service/rooms.js | 11 +---------- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index c2623eb9..0ca7e951 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -52,10 +52,12 @@ const sendNotificationByTopic = async (data, topic) => { * @param {string} title - 보낼 메시지의 제목입니다. * @param {string} body - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @param {string?} url - 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ -const sendMessageByToken = async (token, title, body, icon) => { - const data = { title, body, icon }; +const sendMessageByToken = async (token, title, body, icon, url) => { + url = url || "/myroom"; + const data = { title, body, icon, url }; return await sendNotificationByToken(data, token); }; @@ -65,10 +67,12 @@ const sendMessageByToken = async (token, title, body, icon) => { * @param {string} title - 보낼 메시지의 제목입니다. * @param {string} body - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @param {string?} url - 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 알림 전송에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ -const sendMessageByTokens = async (tokens, title, body, icon) => { - const data = { title, body, icon }; +const sendMessageByTokens = async (tokens, title, body, icon, url) => { + url = url || "/myroom"; + const data = { title, body, icon, url }; return await sendNotificationByTokens(data, tokens); }; @@ -76,18 +80,21 @@ const sendMessageByTokens = async (tokens, title, body, icon) => { * 주어진 사용자를 특정한 topic에 구독시킵니다. * @param {string} userId - topic을 구독할 사용자의 ObjectId입니다. * @param {string} topic - 구독할 topic입니다. - * @return {Promise} 토픽 구독에 성공했으면 true, 아니면 false를 반환합니다. + * @return {Promise} 토픽 구독에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ const subscribeUserToTopic = async (userId, topic) => { try { const deviceToken = await deviceTokenModel.findOne({ userId, }); - await getMessaging().subscribeToTopic(deviceToken.deviceToken, topic); - return true; + const { successCount } = await getMessaging().subscribeToTopic( + deviceToken.deviceToken, + topic + ); + return successCount; } catch (error) { logger.error(error); - return false; + return -1; } }; @@ -95,18 +102,21 @@ const subscribeUserToTopic = async (userId, topic) => { * 주어진 사용자를 특정한 topic으로부터 구독 해제시킵니다. * @param {string} userId - topic을 구독 해제할 사용자의 id입니다. * @param {string} topic - 구독을 해제할 topic입니다. - * @return {Promise} 토픽 구독 해제에 성공했으면 true, 아니면 false를 반환합니다. + * @return {Promise} 토픽 구독 해제에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ const unsubscribeUserFromTopic = async (userId, topic) => { try { const deviceToken = await deviceTokenModel.findOne({ userId, }); - await getMessaging().unsubscribeFromTopic(deviceToken.deviceToken, topic); - return true; + const { successCount } = await getMessaging().unsubscribeFromTopic( + deviceToken.deviceToken, + topic + ); + return successCount; } catch (error) { logger.error(error); - return false; + return -1; } }; @@ -116,10 +126,12 @@ const unsubscribeUserFromTopic = async (userId, topic) => { * @param {string} title - 보낼 메시지의 제목입니다. * @param {string} body - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @param {string?} url - 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ -const sendMessageByTopic = async (topic, title, body, icon) => { - const data = { title, body, icon }; +const sendMessageByTopic = async (topic, title, body, icon, url) => { + url = url || "/myroom"; + const data = { title, body, icon, url }; return await sendNotificationByTopic(data, topic); }; diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index c1d14527..5de09d98 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -2,7 +2,7 @@ const { getLoginInfo, joinChatRoom, leaveChatRoom } = require("../auth/login"); const { roomModel, userModel, chatModel } = require("../db/mongo"); const { getS3Url } = require("../db/awsS3"); const validator = require("validator"); -const { sendMessageByTopic, sendMessageByTokens } = require("../modules/fcm"); +const { subscribeUserToTopic, sendMessageByTopic } = require("../modules/fcm"); const logger = require("../modules/logger"); class IllegalArgumentsException { @@ -118,6 +118,9 @@ const ioListeners = (io, socket) => { socket.join(`chatRoom-${roomId}`); session.save(); // Socket.io 세션의 변경 사항을 Express 세션에 반영. + // 방을 추가한 사용자를 `room-${room._id}` topic에 구독시킵니다. + await subscribeUserToTopic(myUser._id, `room-${room._id}`); + const amount = 30; const chats = await chatModel .find({ roomId: roomId, isValid: true }) @@ -186,12 +189,12 @@ const ioListeners = (io, socket) => { io.to(socket.id).emit("chats-send", { done: true }); // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. - const myRoom = await roomModel.findById(roomId, "name"); await sendMessageByTopic( `room-${roomId}`, - myRoom.name, + myUser.nickname, chatMessage.content, - getS3Url(`/profile-img/${myUser.profileImageUrl}`) + getS3Url(`/profile-img/${myUser.profileImageUrl}`), + `/myroom/${roomId}` ); } catch (err) { logger.error(err); diff --git a/src/service/rooms.js b/src/service/rooms.js index bc801fb6..fa6e59a7 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -1,10 +1,7 @@ const { roomModel, locationModel, userModel } = require("../db/mongo"); const { emitChatEvent } = require("../route/chats.socket"); const { leaveChatRoom } = require("../auth/login"); -const { - subscribeUserToTopic, - unsubscribeUserFromTopic, -} = require("../modules/fcm"); +const { unsubscribeUserFromTopic } = require("../modules/fcm"); const logger = require("../modules/logger"); const { roomPopulateOption, @@ -80,9 +77,6 @@ const createHandler = async (req, res) => { authorId: user._id, }); - // 방을 추가한 사용자를 `room-${room._id}` topic에 구독시킵니다. - logger.info(await subscribeUserToTopic(user._id, `room-${room._id}`)); - const roomObject = (await room.populate(roomPopulateOption)).toObject(); return res.send(formatSettlement(roomObject)); } catch (err) { @@ -177,9 +171,6 @@ const joinHandler = async (req, res) => { authorId: user._id, }); - // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. - logger.info(await subscribeUserToTopic(user._id, `room-${room._id}`)); - const roomObject = (await room.populate(roomPopulateOption)).toObject(); res.send(formatSettlement(roomObject)); } catch (err) { From c299ff9708ebbd95d795c4ea235f9ce4c8093a39 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 15 Feb 2023 14:57:21 +0900 Subject: [PATCH 097/308] Add: subsribe to topic when entering the room --- src/route/chats.socket.js | 19 ++++++++++++++----- src/route/rooms.js | 2 +- src/service/rooms.js | 22 ++++++++++++++++++++-- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 5de09d98..2c8c79d0 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -118,8 +118,14 @@ const ioListeners = (io, socket) => { socket.join(`chatRoom-${roomId}`); session.save(); // Socket.io 세션의 변경 사항을 Express 세션에 반영. - // 방을 추가한 사용자를 `room-${room._id}` topic에 구독시킵니다. - await subscribeUserToTopic(myUser._id, `room-${room._id}`); + // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. + const topic = `room-${room._id}`; + logger.info( + `${await subscribeUserToTopic( + myUser._id, + topic + )} tokens subscribed to ${topic}` + ); const amount = 30; const chats = await chatModel @@ -189,12 +195,15 @@ const ioListeners = (io, socket) => { io.to(socket.id).emit("chats-send", { done: true }); // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. + const room = await roomModel.findById(roomId, "name"); + const topic = `room-${roomId}`; + const urlOnClick = `/myroom/${roomId}`; await sendMessageByTopic( - `room-${roomId}`, - myUser.nickname, + topic, + `${myUser.nickname} (${room.name})`, chatMessage.content, getS3Url(`/profile-img/${myUser.profileImageUrl}`), - `/myroom/${roomId}` + urlOnClick ); } catch (err) { logger.error(err); diff --git a/src/route/rooms.js b/src/route/rooms.js index d43e4352..58b8115c 100644 --- a/src/route/rooms.js +++ b/src/route/rooms.js @@ -3,8 +3,8 @@ const { query, body } = require("express-validator"); const router = express.Router(); const roomHandlers = require("../service/rooms"); -const validator = require("../middleware/validator"); const patterns = require("../db/patterns"); +const validator = require("../middleware/validator"); const setTimestamp = require("../middleware/setTimestamp"); // 라우터 접근 시 로그인 필요 diff --git a/src/service/rooms.js b/src/service/rooms.js index fa6e59a7..557c7bc0 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -1,7 +1,10 @@ const { roomModel, locationModel, userModel } = require("../db/mongo"); const { emitChatEvent } = require("../route/chats.socket"); const { leaveChatRoom } = require("../auth/login"); -const { unsubscribeUserFromTopic } = require("../modules/fcm"); +const { + subscribeUserToTopic, + unsubscribeUserFromTopic, +} = require("../modules/fcm"); const logger = require("../modules/logger"); const { roomPopulateOption, @@ -171,6 +174,15 @@ const joinHandler = async (req, res) => { authorId: user._id, }); + // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. + const topic = `room-${room._id}`; + logger.info( + `${await subscribeUserToTopic( + user._id, + topic + )} tokens subscribed to ${topic}` + ); + const roomObject = (await room.populate(roomPopulateOption)).toObject(); res.send(formatSettlement(roomObject)); } catch (err) { @@ -264,7 +276,13 @@ const abortHandler = async (req, res) => { }); // 방에서 나간 사용자를 `room-${room._id}` topic으로부터 구독 해제시킵니다. - logger.info(await unsubscribeUserFromTopic(user._id, `room-${room._id}`)); + const topic = `room-${room._id}`; + logger.info( + `${await unsubscribeUserFromTopic( + user._id, + topic + )} tokens unsubscribed from ${topic}` + ); const roomObject = (await room.populate(roomPopulateOption)).toObject(); const isOver = getIsOver(roomObject, user.id); From 9728cca69edd91567cedec120cf2f2ef7689da43 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 15 Feb 2023 11:16:25 +0000 Subject: [PATCH 098/308] Refactor: rooms.js --- test/rooms.js | 93 +++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 48 deletions(-) diff --git a/test/rooms.js b/test/rooms.js index c83daf72..f6693785 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -9,7 +9,7 @@ const httpMocks = require("node-mocks-http"); let testData = { rooms: [], users: [], chat: [], location: [], report: [] }; // rooms.js 관련 8개의 handler을 테스트 -// 1. test1이 1분 뒤에 출발하는 test-room 방을 생성 +// 1. test1이 1분 뒤에 출발하는 test-room 방을 생성, 제대로 생성 되었는지 확인 describe("[rooms] 1.createHandler", () => { it("should create room which departs after 1 minute", async () => { const testUser1 = await userGenerator("test1", testData); @@ -31,11 +31,12 @@ describe("[rooms] 1.createHandler", () => { const testRoom = await roomModel.findOne({ name: "test-room" }); testData["rooms"].push(testRoom); - expect(res._getData()).to.has.property("name", "test-room"); + const resData = res._getData(); + expect(resData).to.has.property("name", "test-room"); }); }); -// 2. test1을 통하여 방의 정보 가져옴 +// 2. test1을 통하여 방의 정보를 제대로 가져오는지 확인 describe("[rooms] 2.infoHandler", () => { it("should return information of room", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); @@ -47,12 +48,13 @@ describe("[rooms] 2.infoHandler", () => { let res = httpMocks.createResponse(); await roomsHandlers.infoHandler(req, res); - expect(res._getData()).to.has.property("name", "test-room"); - expect(res._getData()).to.has.property("isOver"); + const resData = res._getData(); + expect(resData).to.has.property("name", "test-room"); + expect(resData).to.has.property("isOver"); }); }); -// 3. test2가 test-room에 join +// 3. test2가 test-room에 join, 방에 잘 join 했는지 확인 describe("[rooms] 3.joinHandler", () => { it("should return information of room and join", async () => { const testUser2 = await userGenerator("test2", testData); @@ -65,15 +67,15 @@ describe("[rooms] 3.joinHandler", () => { app, }); let res = httpMocks.createResponse(); - await roomsHandlers.joinHandler(req, res); - expect(res._getData()).to.has.property("name", "test-room"); - expect(res._getData().part).to.have.lengthOf(2); + const resData = res._getData(); + expect(resData).to.has.property("name", "test-room"); + expect(resData.part).to.have.lengthOf(2); }); }); -// 4. 방의 정보를 통해 검색 +// 4. 방의 정보를 통해 검색, 검색 정보가 예상과 일치하는지 확인 describe("[rooms] 4.searchHandler", () => { it("should return information of searching room", async () => { const testFrom = await locationModel.findOne({ koName: "대전역" }); @@ -102,62 +104,58 @@ describe("[rooms] 4.searchHandler", () => { describe("[rooms] 5.searchByUserHandler", () => { it("should return information of searching room", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); - const req = { + let req = httpMocks.createRequest({ userId: testUser1.id, - }; - const res = { - json: (data) => { - expect(data["ongoing"][0]).to.has.property("name", "test-room"); - expect(data["done"][0]).to.be.an("undefined"); - }, - }; - + }); + let res = httpMocks.createResponse(); await roomsHandlers.searchByUserHandler(req, res); + + const resJson = res._getJSONData(); + expect(resJson["ongoing"][0]).to.has.property("name", "test-room"); + expect(resJson["done"][0]).to.be.undefined; }); }); -// 6.1분이 지난 후, 정산 정보를 불러옴 +// 6.1분이 지난 후, 정산 정보를 불러옴. 예상과 같은 정보를 불러오는지 확인 describe("[rooms] 6.commitPaymentHandler", () => { it("should return information of room and commit payment", async () => { const testUser1 = await userModel.findOne({ id: "test1" }); const testRoom = await roomModel.findOne({ name: "test-room" }); - const req = { + let req = httpMocks.createRequest({ body: { roomId: testRoom._id }, userId: testUser1.id, timestamp: Date.now() + 60 * 1000, - }; - const res = { - send: (data) => { - expect(data).to.has.property("name", "test-room"); - expect(data).to.has.property("isOver", true); - expect(data).to.has.property("settlementTotal", 1); - }, - }; + }); + let res = httpMocks.createResponse(); await roomsHandlers.commitPaymentHandler(req, res); + + const resData = res._getData(); + expect(resData).to.has.property("name", "test-room"); + expect(resData).to.has.property("isOver", true); + expect(resData).to.has.property("settlementTotal", 1); }); }); -// 7. 도착 정보를 불러옴 +// 7. 도착 정보를 불러옴. 예상과 같은 정보를 불러오는지 확인 describe("[rooms] 7.settlementHandler", () => { it("should return information of room and set settlement", async () => { const testUser2 = await userModel.findOne({ id: "test2" }); const testRoom = await roomModel.findOne({ name: "test-room" }); - const req = { + let req = httpMocks.createRequest({ body: { roomId: testRoom._id }, userId: testUser2.id, - }; - const res = { - send: (data) => { - expect(data).to.has.property("name", "test-room"); - expect(data).to.has.property("isOver", true); - expect(data).to.has.property("settlementTotal", 2); - }, - }; + }); + let res = httpMocks.createResponse(); await roomsHandlers.settlementHandler(req, res); + + const resData = res._getData(); + expect(resData).to.has.property("name", "test-room"); + expect(resData).to.has.property("isOver", true); + expect(resData).to.has.property("settlementTotal", 2); }); }); -// 8. test2 방에서 퇴장, 생성해준 data 모두 삭제 +// 8. test2 방에서 퇴장, 제대로 방에서 나갔는지 확인하고 생성해준 data 모두 삭제 describe("[rooms] 8.abortHandler", () => { const removeTestData = async () => { // drop all testData @@ -166,19 +164,18 @@ describe("[rooms] 8.abortHandler", () => { it("should return information of room and abort user", async () => { const testUser2 = await userModel.findOne({ id: "test2" }); const testRoom = await roomModel.findOne({ name: "test-room" }); - const req = { + let req = httpMocks.createRequest({ body: { roomId: testRoom._id }, userId: testUser2.id, session: {}, app, - }; - const res = { - send: (data) => { - expect(data).to.has.property("name", "test-room"); - expect(data.part).to.have.lengthOf(1); - }, - }; + }); + let res = httpMocks.createResponse(); await roomsHandlers.abortHandler(req, res); afterEach(removeTestData); + + const resData = res._getData(); + expect(resData).to.has.property("name", "test-room"); + expect(resData.part).to.have.lengthOf(1); }); }); From 99ca1352029ba27c5991707a690612e7a0aefc07 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 15 Feb 2023 11:27:47 +0000 Subject: [PATCH 099/308] Refactor: add comment and unify code style --- test/auth.replace.js | 2 ++ test/locations.js | 2 ++ test/logininfo.js | 7 +++++++ test/reports.js | 11 +++++++---- test/users.js | 22 ++++++++++++---------- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/test/auth.replace.js b/test/auth.replace.js index c8d7545f..b3db78f1 100644 --- a/test/auth.replace.js +++ b/test/auth.replace.js @@ -4,6 +4,8 @@ const authHandlers = require("../src/service/auth.replace"); const { userModel } = require("../src/db/mongo"); const security = require("../security"); +// auth.replace.js 관련 1개의 handler을 테스트 +// 1. dev 환경에서 front의 URL로 잘 redirect 되는지 확인 describe("[auth.replace] 1.sparcsssoHandler", () => { const removeTestUser = async () => { // drop all collections diff --git a/test/locations.js b/test/locations.js index 1d9c0e24..1362f9ce 100644 --- a/test/locations.js +++ b/test/locations.js @@ -2,6 +2,8 @@ const expect = require("chai").expect; const locationHandlers = require("../src/service/locations"); const httpMocks = require("node-mocks-http"); +// locations.js 관련 1개의 handler을 테스트 +// 1. 모든 location 정보를 잘 가져오는지 확인 describe("[locations] 1.getAllLocationsHandler", () => { it("should return information of locations correctly", async () => { let req = httpMocks.createRequest({}); diff --git a/test/logininfo.js b/test/logininfo.js index 6f2ab0ff..f2f19a2c 100644 --- a/test/logininfo.js +++ b/test/logininfo.js @@ -2,6 +2,10 @@ const expect = require("chai").expect; const logininfoHandlers = require("../src/service/logininfo"); const { userModel } = require("../src/db/mongo"); +// logininfo.js 관련 2개의 handler을 테스트 (동기식이므로 httpMocks 사용하지 않음) +// 1-1. 로그인 한 유저가 없을 시 undefined를 return 하는지 확인 +// 1-2. login 정보를 잘 return 하는지 확인 +// 1-3. 세션이 만료됐을 때 undefined를 잘 return 하는지 확인 describe("[logininfo] 1.logininfoHandler", () => { it("should return {id: undefined, sid: undefined, name: undefined } when no user is logged in", () => { const req = { session: {} }; @@ -64,6 +68,9 @@ describe("[logininfo] 1.logininfoHandler", () => { }); }); +// 2-1. 로그인 한 유저가 없을 시 undefined를 return 하는지 확인 +// 2-2. login detail 정보를 잘 return 하는지 확인 +// 2-3. 세션이 만료됐을 때 undefined를 잘 return 하는지 확인 describe("[logininfo] 2.detailHandler", () => { it("should return { id: undefined } when no user is logged in", () => { const req = { session: {} }; diff --git a/test/reports.js b/test/reports.js index 08c19ccf..9c253277 100644 --- a/test/reports.js +++ b/test/reports.js @@ -9,6 +9,7 @@ const removeTestData = async () => { await testRemover(testData); }; +// reports.js 관련 2개의 handler을 테스트 // 1. test1 유저가 test2 유저를 미결제로 신고, 성공 메세지가 제대로 오는지 확인 describe("[reports] 1.createHandler", () => { it("should return correct response from handler", async () => { @@ -27,8 +28,9 @@ describe("[reports] 1.createHandler", () => { let res = httpMocks.createResponse(); await reportHandlers.createHandler(req, res); + const resData = res._getData(); expect(res).to.has.property("statusCode", 200); - expect(res._getData()).to.equal(msg); + expect(resData).to.equal(msg); }); }); @@ -43,10 +45,11 @@ describe("[reports] 2.searchByUserHandler", () => { await reportHandlers.searchByUserHandler(req, res); afterEach(removeTestData); + const resJson = res._getJSONData(); expect(res).to.has.property("statusCode", 200); - expect(res._getJSONData()).to.has.property("reporting"); - expect(res._getJSONData()).to.has.property("reported"); - expect(res._getJSONData().reporting[0]).to.has.property( + expect(resJson).to.has.property("reporting"); + expect(resJson).to.has.property("reported"); + expect(resJson.reporting[0]).to.has.property( "creatorId", testUser1._id.toString() ); diff --git a/test/users.js b/test/users.js index 5fa5237e..aa78f26a 100644 --- a/test/users.js +++ b/test/users.js @@ -22,8 +22,9 @@ describe("[users] 1.agreeOnTermsOfServiceHandler", () => { let res = httpMocks.createResponse(); await usersHandlers.agreeOnTermsOfServiceHandler(req, res); + const resData = res._getData(); expect(res).to.has.property("statusCode", 200); - expect(res._getData()).to.equal(msg); + expect(resData).to.equal(msg); }); }); @@ -37,8 +38,9 @@ describe("[users] 2.getAgreeOnTermsOfServiceHandler", () => { let res = httpMocks.createResponse(); await usersHandlers.getAgreeOnTermsOfServiceHandler(req, res); + const resJson = res._getJSONData(); expect(res).to.has.property("statusCode", 200); - expect(res._getJSONData()).to.has.property("agreeOnTermsOfService", true); + expect(resJson).to.has.property("agreeOnTermsOfService", true); }); }); @@ -58,8 +60,9 @@ describe("[users] 3.editNicknameHandler", () => { let res = httpMocks.createResponse(); await usersHandlers.editNicknameHandler(req, res); + const resData = res._getData(); expect(res).to.has.property("statusCode", 200); - expect(res._getData()).to.equal(msg); + expect(resData).to.equal(msg); }); it("should be changed to new nickname", async () => { @@ -84,8 +87,9 @@ describe("[users] 4.editAccountHandler", () => { let res = httpMocks.createResponse(); await usersHandlers.editAccountHandler(req, res); + const resData = res._getData(); expect(res).to.has.property("statusCode", 200); - expect(res._getData()).to.equal(msg); + expect(resData).to.equal(msg); }); it("should be changed to new account", async () => { @@ -109,16 +113,14 @@ describe("[users] 5.editProfileImgGetPUrlHandler", () => { let res = httpMocks.createResponse(); await usersHandlers.editProfileImgGetPUrlHandler(req, res); + const resJson = res._getJSONData(); expect(res).to.has.property("statusCode", 200); - expect(res._getJSONData()).to.has.property("url"); - expect(res._getJSONData().fields).to.has.property( + expect(resJson).to.has.property("url"); + expect(resJson.fields).to.has.property( "key", `profile-img/${testUser1._id}` ); - expect(res._getJSONData().fields).to.has.property( - "Content-Type", - testImgType - ); + expect(resJson.fields).to.has.property("Content-Type", testImgType); }); }); From 17fbb0ab17fdab84bd6afa92b8735ce39ad63e38 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 15 Feb 2023 21:04:26 +0900 Subject: [PATCH 100/308] Add: subsribe to room topics when registering token --- src/modules/fcm.js | 31 +++++++++++++++++++++++++++++++ src/route/chats.socket.js | 11 +---------- src/service/auth.js | 13 ++++++++++++- src/service/auth.replace.js | 15 ++++++++++++++- src/service/rooms.js | 21 ++------------------- 5 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 0ca7e951..93f578fa 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -91,6 +91,7 @@ const subscribeUserToTopic = async (userId, topic) => { deviceToken.deviceToken, topic ); + logger.info(`${userId}'s ${successCount} token(s) subscribed to ${topic}`); return successCount; } catch (error) { logger.error(error); @@ -98,6 +99,32 @@ const subscribeUserToTopic = async (userId, topic) => { } }; +/** + * 주어진 사용자를 주어진 roomId들에 해당하는 topic들에 구독시킵니다. + * @summary subscribeUserToTopic의 기능을 연장한 함수입니다. + * @param {string} userId - topic을 구독할 사용자의 ObjectId입니다. + * @param {Array} roomIds: 구독할 room의 ObjectId들로 구성된 Array입니다. + * @return {Promise} 토픽 구독에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. + */ +const subscribeUserToRoomTopics = async (userId, roomIds) => { + try { + const result = await Promise.all( + roomIds.map(async (roomId) => { + const topic = `room-${roomId}`; + return await subscribeUserToTopic(userId, topic); + }) + ); + if (result.includes(-1)) { + return -1; + } else { + return result.reduce((a, b) => a + b, 0); + } + } catch (error) { + logger.error(error); + return -1; + } +}; + /** * 주어진 사용자를 특정한 topic으로부터 구독 해제시킵니다. * @param {string} userId - topic을 구독 해제할 사용자의 id입니다. @@ -113,6 +140,9 @@ const unsubscribeUserFromTopic = async (userId, topic) => { deviceToken.deviceToken, topic ); + logger.info( + `${userId}'s ${successCount} token(s) unsubscribed from ${topic}` + ); return successCount; } catch (error) { logger.error(error); @@ -139,6 +169,7 @@ module.exports = { sendMessageByToken, sendMessageByTokens, subscribeUserToTopic, + subscribeUserToRoomTopics, unsubscribeUserFromTopic, sendMessageByTopic, }; diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 2c8c79d0..1e0a9f75 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -2,7 +2,7 @@ const { getLoginInfo, joinChatRoom, leaveChatRoom } = require("../auth/login"); const { roomModel, userModel, chatModel } = require("../db/mongo"); const { getS3Url } = require("../db/awsS3"); const validator = require("validator"); -const { subscribeUserToTopic, sendMessageByTopic } = require("../modules/fcm"); +const { sendMessageByTopic } = require("../modules/fcm"); const logger = require("../modules/logger"); class IllegalArgumentsException { @@ -118,15 +118,6 @@ const ioListeners = (io, socket) => { socket.join(`chatRoom-${roomId}`); session.save(); // Socket.io 세션의 변경 사항을 Express 세션에 반영. - // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. - const topic = `room-${room._id}`; - logger.info( - `${await subscribeUserToTopic( - myUser._id, - topic - )} tokens subscribed to ${topic}` - ); - const amount = 30; const chats = await chatModel .find({ roomId: roomId, isValid: true }) diff --git a/src/service/auth.js b/src/service/auth.js index 045a34c7..a76bbe46 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -7,6 +7,7 @@ const { getFullUsername, } = require("../modules/modifyProfile"); +const { subscribeUserToRoomTopics } = require("../modules/fcm"); const jwt = require("../modules/jwt"); const APP_URI_SCHEME = require("../../security").appUriScheme; @@ -173,7 +174,9 @@ const registerDeviceTokenHandler = async (req, res) => { try { const newDeviceToken = req.body.deviceToken; // 데이터베이스에 새 레코드를 추가합니다. - const user = await userModel.findOne({ id: req.userId }, "_id"); + const user = await userModel + .findOne({ id: req.userId }, "_id ongoingRoom doneRoom") + .lean(); const deviceToken = await deviceTokenModel.updateOne( { userId: user._id, @@ -184,6 +187,14 @@ const registerDeviceTokenHandler = async (req, res) => { }, { upsert: true, new: true } ); + // 사용자의 모든 deviceToken들을 사용자가 참여중인 방들에 해당하는 topic에 구독시킵니다. + const ongoingRoom = user.ongoingRoom; + const doneRoom = user.doneRoom; + const roomIds = ongoingRoom + .concat(doneRoom) + .map((objectId) => objectId.toString()); + await subscribeUserToRoomTopics(user._id, roomIds); + return res.status(200).json({ deviceToken: deviceToken.deviceToken, }); diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index a70197c7..0cd7738a 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -5,6 +5,8 @@ const { generateNickname, generateProfileImageUrl, } = require("../modules/modifyProfile"); + +const { subscribeUserToRoomTopics } = require("../modules/fcm"); const logger = require("../modules/logger"); const loginHtml = ` @@ -137,7 +139,10 @@ const registerDeviceTokenHandler = async (req, res) => { try { const newDeviceToken = req.body.deviceToken; // 데이터베이스에 새 레코드를 추가합니다. - const user = await userModel.findOne({ id: req.userId }, "_id"); + const user = await userModel.findOne( + { id: req.userId }, + "_id ongoingRoom doneRoom" + ); const deviceToken = await deviceTokenModel.updateOne( { userId: user._id, @@ -148,6 +153,14 @@ const registerDeviceTokenHandler = async (req, res) => { }, { upsert: true, new: true } ); + // 사용자의 모든 deviceToken들을 사용자가 참여중인 방들에 해당하는 topic에 구독시킵니다. + const ongoingRoom = user.ongoingRoom; + const doneRoom = user.doneRoom; + const roomIds = ongoingRoom + .concat(doneRoom) + .map((objectId) => objectId.toString()); + await subscribeUserToRoomTopics(user._id, roomIds); + return res.status(200).json({ deviceToken: deviceToken.deviceToken, }); diff --git a/src/service/rooms.js b/src/service/rooms.js index 557c7bc0..40b36735 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -1,10 +1,7 @@ const { roomModel, locationModel, userModel } = require("../db/mongo"); const { emitChatEvent } = require("../route/chats.socket"); const { leaveChatRoom } = require("../auth/login"); -const { - subscribeUserToTopic, - unsubscribeUserFromTopic, -} = require("../modules/fcm"); +const { unsubscribeUserFromTopic } = require("../modules/fcm"); const logger = require("../modules/logger"); const { roomPopulateOption, @@ -174,15 +171,6 @@ const joinHandler = async (req, res) => { authorId: user._id, }); - // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. - const topic = `room-${room._id}`; - logger.info( - `${await subscribeUserToTopic( - user._id, - topic - )} tokens subscribed to ${topic}` - ); - const roomObject = (await room.populate(roomPopulateOption)).toObject(); res.send(formatSettlement(roomObject)); } catch (err) { @@ -277,12 +265,7 @@ const abortHandler = async (req, res) => { // 방에서 나간 사용자를 `room-${room._id}` topic으로부터 구독 해제시킵니다. const topic = `room-${room._id}`; - logger.info( - `${await unsubscribeUserFromTopic( - user._id, - topic - )} tokens unsubscribed from ${topic}` - ); + await unsubscribeUserFromTopic(user._id, topic); const roomObject = (await room.populate(roomPopulateOption)).toObject(); const isOver = getIsOver(roomObject, user.id); From 26e288b37bf230dfa6c3960b32ed679466fe208c Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 15 Feb 2023 22:57:09 +0900 Subject: [PATCH 101/308] Add: save token-topic pair to DB --- src/db/mongo.js | 14 ++++++++++++++ src/modules/fcm.js | 42 +++++++++++++++++++++++++++++++++++++----- src/service/auth.js | 7 ++++--- src/service/rooms.js | 13 ++++++++++++- 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/db/mongo.js b/src/db/mongo.js index 477adf36..da3f3c52 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -51,6 +51,16 @@ const deviceTokenSchema = Schema({ }, }); +const topicSubscriptionSchema = Schema({ + deviceToken: String, + topic: String, + subscribedAt: { + type: Date, + default: () => Date.now(), + required: true, + }, +}); + const roomSchema = Schema({ name: { type: String, required: true, default: "이름 없음", text: true }, from: { type: Schema.Types.ObjectId, ref: "Location", required: true }, @@ -132,6 +142,10 @@ mongoose.connect(security.mongo, { module.exports = { userModel: mongoose.model("User", userSchema), deviceTokenModel: mongoose.model("DeviceToken", deviceTokenSchema), + topicSubscriptionModel: mongoose.model( + "TopicSubscription", + topicSubscriptionSchema + ), roomModel: mongoose.model("Room", roomSchema), locationModel: mongoose.model("Location", locationSchema), chatModel: mongoose.model("Chat", chatSchema), diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 93f578fa..36cd1f3e 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -1,5 +1,5 @@ const { getMessaging } = require("firebase-admin/messaging"); -const { deviceTokenModel } = require("../db/mongo"); +const { deviceTokenModel, topicSubscriptionModel } = require("../db/mongo"); const logger = require("../modules/logger"); const sendNotificationByToken = async (data, token) => { @@ -84,13 +84,34 @@ const sendMessageByTokens = async (tokens, title, body, icon, url) => { */ const subscribeUserToTopic = async (userId, topic) => { try { - const deviceToken = await deviceTokenModel.findOne({ + const { deviceToken } = await deviceTokenModel.findOne({ userId, }); const { successCount } = await getMessaging().subscribeToTopic( - deviceToken.deviceToken, + deviceToken, topic ); + + // 데이터베이스에 해당 토큰에 대한 토픽 구독 레코드를 추가합니다. + await Promise.all( + deviceToken.map(async (token) => { + return await topicSubscriptionModel.updateOne( + { + deviceToken: token, + topic: topic, + }, + { + deviceToken: token, + topic: topic, + }, + { + upsert: true, + new: true, + } + ); + }) + ); + logger.info(`${userId}'s ${successCount} token(s) subscribed to ${topic}`); return successCount; } catch (error) { @@ -133,13 +154,24 @@ const subscribeUserToRoomTopics = async (userId, roomIds) => { */ const unsubscribeUserFromTopic = async (userId, topic) => { try { - const deviceToken = await deviceTokenModel.findOne({ + const { deviceToken } = await deviceTokenModel.findOne({ userId, }); const { successCount } = await getMessaging().unsubscribeFromTopic( - deviceToken.deviceToken, + deviceToken, topic ); + + // 데이터베이스에서 해당 토큰에 대한 토픽 구독 레코드를 삭제합니다. + await Promise.all( + deviceToken.map(async (token) => { + return await topicSubscriptionModel.deleteOne({ + deviceToken: token, + topic: topic, + }); + }) + ); + logger.info( `${userId}'s ${successCount} token(s) unsubscribed from ${topic}` ); diff --git a/src/service/auth.js b/src/service/auth.js index a76bbe46..fb093eda 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -174,9 +174,10 @@ const registerDeviceTokenHandler = async (req, res) => { try { const newDeviceToken = req.body.deviceToken; // 데이터베이스에 새 레코드를 추가합니다. - const user = await userModel - .findOne({ id: req.userId }, "_id ongoingRoom doneRoom") - .lean(); + const user = await userModel.findOne( + { id: req.userId }, + "_id ongoingRoom doneRoom" + ); const deviceToken = await deviceTokenModel.updateOne( { userId: user._id, diff --git a/src/service/rooms.js b/src/service/rooms.js index 40b36735..ec0767a0 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -1,7 +1,10 @@ const { roomModel, locationModel, userModel } = require("../db/mongo"); const { emitChatEvent } = require("../route/chats.socket"); const { leaveChatRoom } = require("../auth/login"); -const { unsubscribeUserFromTopic } = require("../modules/fcm"); +const { + subscribeUserToTopic, + unsubscribeUserFromTopic, +} = require("../modules/fcm"); const logger = require("../modules/logger"); const { roomPopulateOption, @@ -77,6 +80,10 @@ const createHandler = async (req, res) => { authorId: user._id, }); + // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. + const topic = `room-${room._id}`; + await subscribeUserToTopic(user._id, topic); + const roomObject = (await room.populate(roomPopulateOption)).toObject(); return res.send(formatSettlement(roomObject)); } catch (err) { @@ -171,6 +178,10 @@ const joinHandler = async (req, res) => { authorId: user._id, }); + // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. + const topic = `room-${room._id}`; + await subscribeUserToTopic(user._id, topic); + const roomObject = (await room.populate(roomPopulateOption)).toObject(); res.send(formatSettlement(roomObject)); } catch (err) { From 614922a9ca7a2936e6e30fe45d22554277c9f0b0 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 15 Feb 2023 23:25:29 +0900 Subject: [PATCH 102/308] Refactor: rewrite chat notification by tokens instead of topics --- src/modules/fcm.js | 111 ++++++++++++++++++++---------------- src/route/chats.socket.js | 15 +++-- src/service/auth.js | 2 - src/service/auth.replace.js | 2 - src/service/rooms.js | 16 ------ 5 files changed, 72 insertions(+), 74 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 36cd1f3e..1fe61c54 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -2,6 +2,32 @@ const { getMessaging } = require("firebase-admin/messaging"); const { deviceTokenModel, topicSubscriptionModel } = require("../db/mongo"); const logger = require("../modules/logger"); +/** + * 사용자들의 ObjectId의 배열이 주어졌을 때, 해당 사용자들의 모든 deviceToken을 하나의 Array로 반환합니다. + * @param {Array} userIds - 사용자의 ObjectId로 이루어진 Array입니다. + * @return {Promise>} deviceToken의 Array를 반환합니다. 오류가 발생하면 빈 배열을 반환합니다. + */ +const getTokensOfUsers = async (userIds) => { + const deviceTokenOfUsers = await Promise.all( + userIds.map(async (userId) => { + const deviceToken = await deviceTokenModel.findOne({ + userId, + }); + return deviceToken.deviceToken; + }) + ); + return deviceTokenOfUsers.reduce( + (arrayA, arrayB) => arrayA.concat(arrayB), + new Array() + ); +}; + +/** + * 주어진 token에 해당하는 기기에 data를 전송합니다. + * @param {string} token - 알림을 받을 기기의 deviceToken입니다. + * @param {Object} data - 기기에 전송할 key-value pair입니다. 모든 value는 string 타입이어야 합니다. + * @return {Promise} data 전송에 성공했으면 true, 아니면 false를 반환합니다. + */ const sendNotificationByToken = async (data, token) => { try { const message = { @@ -17,8 +43,15 @@ const sendNotificationByToken = async (data, token) => { } }; +/** + * 주어진 token들에 해당하는 기기들에 data를 전송합니다. + * @param {Array} tokens - 알림을 받을 기기들의 deviceToken들입니다. + * @param {Object} data - 기기에 전송할 key-value pair입니다. 모든 value는 string 타입이어야 합니다. + * @return {Promise} data 전송에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. + */ const sendNotificationByTokens = async (data, tokens) => { try { + if (tokens.length === 0) return -1; const message = { data, tokens, @@ -31,6 +64,12 @@ const sendNotificationByTokens = async (data, tokens) => { } }; +/** + * 주어진 topic을 구독하고 있는 모든 기기에 data를 전송합니다. + * @param {string} token - data를 보낼 기기들이 구독하고 있는 topic입니다. + * @param {Object} data - 기기에 전송할 key-value pair입니다. 모든 value는 string 타입이어야 합니다. + * @return {Promise} data 전송에 성공했으면 true, 아니면 false를 반환합니다. + */ const sendNotificationByTopic = async (data, topic) => { try { const message = { @@ -48,12 +87,12 @@ const sendNotificationByTopic = async (data, topic) => { /** * 주어진 token에 메시지 알림을 전송합니다. - * @param {string} token - 알림을 받을 기기의 deviceToken입니다. + * @param {string} token - 메시지 알림을 받을 기기의 deviceToken입니다. * @param {string} title - 보낼 메시지의 제목입니다. * @param {string} body - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. - * @param {string?} url - 알림 팝업을 클릭했을 때 이동할 주소입니다. - * @return {Promise} 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. + * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. + * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ const sendMessageByToken = async (token, title, body, icon, url) => { url = url || "/myroom"; @@ -63,12 +102,12 @@ const sendMessageByToken = async (token, title, body, icon, url) => { /** * 주어진 token들에 메시지 알림을 전송합니다. - * @param {string} tokens - 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. + * @param {Array} tokens - 메시지 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. * @param {string} title - 보낼 메시지의 제목입니다. * @param {string} body - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. - * @param {string?} url - 알림 팝업을 클릭했을 때 이동할 주소입니다. - * @return {Promise} 알림 전송에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. + * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. + * @return {Promise} 메시지 알림 전송에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ const sendMessageByTokens = async (tokens, title, body, icon, url) => { url = url || "/myroom"; @@ -76,6 +115,21 @@ const sendMessageByTokens = async (tokens, title, body, icon, url) => { return await sendNotificationByTokens(data, tokens); }; +/** + * 주어진 topic을 구독하고 있는 모든 기기에 메시지 알림을 전송합니다. + * @param {string} topic - 메시지 알림을 보낼 기기들이 구독하고 있는 topic입니다. + * @param {string} title - 보낼 메시지의 제목입니다. + * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. + * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. + */ +const sendMessageByTopic = async (topic, title, body, icon, url) => { + url = url || "/myroom"; + const data = { title, body, icon, url }; + return await sendNotificationByTopic(data, topic); +}; + /** * 주어진 사용자를 특정한 topic에 구독시킵니다. * @param {string} userId - topic을 구독할 사용자의 ObjectId입니다. @@ -120,32 +174,6 @@ const subscribeUserToTopic = async (userId, topic) => { } }; -/** - * 주어진 사용자를 주어진 roomId들에 해당하는 topic들에 구독시킵니다. - * @summary subscribeUserToTopic의 기능을 연장한 함수입니다. - * @param {string} userId - topic을 구독할 사용자의 ObjectId입니다. - * @param {Array} roomIds: 구독할 room의 ObjectId들로 구성된 Array입니다. - * @return {Promise} 토픽 구독에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. - */ -const subscribeUserToRoomTopics = async (userId, roomIds) => { - try { - const result = await Promise.all( - roomIds.map(async (roomId) => { - const topic = `room-${roomId}`; - return await subscribeUserToTopic(userId, topic); - }) - ); - if (result.includes(-1)) { - return -1; - } else { - return result.reduce((a, b) => a + b, 0); - } - } catch (error) { - logger.error(error); - return -1; - } -}; - /** * 주어진 사용자를 특정한 topic으로부터 구독 해제시킵니다. * @param {string} userId - topic을 구독 해제할 사용자의 id입니다. @@ -182,26 +210,11 @@ const unsubscribeUserFromTopic = async (userId, topic) => { } }; -/** - * 주어진 topic을 구독하고 있는 모든 기기에 메시지 알림을 전송합니다. - * @param {string} topic - 알림을 보낼 기기들이 구독하고 있는 topic입니다. - * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} body - 보낼 메시지의 본문입니다. - * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. - * @param {string?} url - 알림 팝업을 클릭했을 때 이동할 주소입니다. - * @return {Promise} 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. - */ -const sendMessageByTopic = async (topic, title, body, icon, url) => { - url = url || "/myroom"; - const data = { title, body, icon, url }; - return await sendNotificationByTopic(data, topic); -}; - module.exports = { + getTokensOfUsers, sendMessageByToken, sendMessageByTokens, + sendMessageByTopic, subscribeUserToTopic, - subscribeUserToRoomTopics, unsubscribeUserFromTopic, - sendMessageByTopic, }; diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 1e0a9f75..206e791e 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -2,7 +2,7 @@ const { getLoginInfo, joinChatRoom, leaveChatRoom } = require("../auth/login"); const { roomModel, userModel, chatModel } = require("../db/mongo"); const { getS3Url } = require("../db/awsS3"); const validator = require("validator"); -const { sendMessageByTopic } = require("../modules/fcm"); +const { getTokensOfUsers, sendMessageByTokens } = require("../modules/fcm"); const logger = require("../modules/logger"); class IllegalArgumentsException { @@ -169,7 +169,7 @@ const ioListeners = (io, socket) => { const myUserId = getLoginInfo({ session: session }).id || ""; const myUser = await userModel.findOne( { id: myUserId }, - "id nickname profileImageUrl" + "_id id nickname profileImageUrl" ); if (!myUser) return io.to(socket.id).emit("chats-send", { err: "user not exist" }); @@ -186,11 +186,16 @@ const ioListeners = (io, socket) => { io.to(socket.id).emit("chats-send", { done: true }); // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. - const room = await roomModel.findById(roomId, "name"); + const room = await roomModel.findById(roomId, "name part"); const topic = `room-${roomId}`; const urlOnClick = `/myroom/${roomId}`; - await sendMessageByTopic( - topic, + const userIdsExceptMe = room.part + .map((participant) => participant.user) + .filter((userId) => userId !== myUser._id); + const deviceTokens = await getTokensOfUsers(userIdsExceptMe); + logger.info(deviceTokens); + await sendMessageByTokens( + deviceTokens, `${myUser.nickname} (${room.name})`, chatMessage.content, getS3Url(`/profile-img/${myUser.profileImageUrl}`), diff --git a/src/service/auth.js b/src/service/auth.js index fb093eda..5f6da88c 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -7,7 +7,6 @@ const { getFullUsername, } = require("../modules/modifyProfile"); -const { subscribeUserToRoomTopics } = require("../modules/fcm"); const jwt = require("../modules/jwt"); const APP_URI_SCHEME = require("../../security").appUriScheme; @@ -194,7 +193,6 @@ const registerDeviceTokenHandler = async (req, res) => { const roomIds = ongoingRoom .concat(doneRoom) .map((objectId) => objectId.toString()); - await subscribeUserToRoomTopics(user._id, roomIds); return res.status(200).json({ deviceToken: deviceToken.deviceToken, diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index 0cd7738a..a2c7a65d 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -6,7 +6,6 @@ const { generateProfileImageUrl, } = require("../modules/modifyProfile"); -const { subscribeUserToRoomTopics } = require("../modules/fcm"); const logger = require("../modules/logger"); const loginHtml = ` @@ -159,7 +158,6 @@ const registerDeviceTokenHandler = async (req, res) => { const roomIds = ongoingRoom .concat(doneRoom) .map((objectId) => objectId.toString()); - await subscribeUserToRoomTopics(user._id, roomIds); return res.status(200).json({ deviceToken: deviceToken.deviceToken, diff --git a/src/service/rooms.js b/src/service/rooms.js index ec0767a0..08b0700f 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -1,10 +1,6 @@ const { roomModel, locationModel, userModel } = require("../db/mongo"); const { emitChatEvent } = require("../route/chats.socket"); const { leaveChatRoom } = require("../auth/login"); -const { - subscribeUserToTopic, - unsubscribeUserFromTopic, -} = require("../modules/fcm"); const logger = require("../modules/logger"); const { roomPopulateOption, @@ -80,10 +76,6 @@ const createHandler = async (req, res) => { authorId: user._id, }); - // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. - const topic = `room-${room._id}`; - await subscribeUserToTopic(user._id, topic); - const roomObject = (await room.populate(roomPopulateOption)).toObject(); return res.send(formatSettlement(roomObject)); } catch (err) { @@ -178,10 +170,6 @@ const joinHandler = async (req, res) => { authorId: user._id, }); - // 방에 참여한 사용자를 `room-${room._id}` topic에 구독시킵니다. - const topic = `room-${room._id}`; - await subscribeUserToTopic(user._id, topic); - const roomObject = (await room.populate(roomPopulateOption)).toObject(); res.send(formatSettlement(roomObject)); } catch (err) { @@ -274,10 +262,6 @@ const abortHandler = async (req, res) => { authorId: user._id, }); - // 방에서 나간 사용자를 `room-${room._id}` topic으로부터 구독 해제시킵니다. - const topic = `room-${room._id}`; - await unsubscribeUserFromTopic(user._id, topic); - const roomObject = (await room.populate(roomPopulateOption)).toObject(); const isOver = getIsOver(roomObject, user.id); From a4e1c60bd4fe1b2295180deda36512e99e42fa21 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 15 Feb 2023 23:49:46 +0900 Subject: [PATCH 103/308] Add: change contents of chat notifications --- src/route/chats.socket.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 206e791e..92737dbd 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -196,8 +196,8 @@ const ioListeners = (io, socket) => { logger.info(deviceTokens); await sendMessageByTokens( deviceTokens, - `${myUser.nickname} (${room.name})`, - chatMessage.content, + room.name, + `${myUser.nickname}: ${chatMessage.content}`, getS3Url(`/profile-img/${myUser.profileImageUrl}`), urlOnClick ); From 2e4bbc8021b5f6057ce47fb4ffadb49e90fd9341 Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 01:18:42 +0900 Subject: [PATCH 104/308] Add: unregister deviceToken on logout --- src/modules/fcm.js | 52 ++++++++++++++++++++++++++++++++++ src/service/auth.js | 56 ++++++++++++++++++------------------- src/service/auth.replace.js | 52 +++++++++++++++++----------------- 3 files changed, 105 insertions(+), 55 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 1fe61c54..bed33d29 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -2,6 +2,56 @@ const { getMessaging } = require("firebase-admin/messaging"); const { deviceTokenModel, topicSubscriptionModel } = require("../db/mongo"); const logger = require("../modules/logger"); +/** + * 사용자의 ObjectId와 FCM device token이 주어졌을 때, 해당 deviceToken을 사용자의 토큰으로 DB에 등록합니다. + * @param {string} userId - 사용자의 ObjectId입니다. + * @param {string} deviceToken - 등록하려는 FCM device token입니다. + * @return {Promise>} 변경된 사용자의 deviceToken의 목록 Array를 반환합니다. 오류가 발생하면 빈 배열을 반환합니다. + */ +const registerDeviceToken = async (userId, deviceToken) => { + try { + const newDeviceToken = await deviceTokenModel.updateOne( + { + userId, + }, + { + userId, + $addToSet: { deviceToken }, + }, + { upsert: true, new: true } + ); + return newDeviceToken.deviceToken; + } catch (error) { + logger.error(error); + return new Array(); + } +}; + +/** + * 사용자의 ObjectId와 FCM device token이 주어졌을 때, 해당 사용자의 해당 deviceToken을 DB에서 삭제합니다. + * @param {string} userId - 사용자의 ObjectId입니다. + * @param {string} deviceToken - 삭제하려는 FCM device token입니다. + * @return {Promise>} 변경된 사용자의 deviceToken의 목록 Array를 반환합니다. 오류가 발생하면 빈 배열을 반환합니다. + */ +const unregisterDeviceToken = async (userId, deviceToken) => { + try { + const newDeviceToken = await deviceTokenModel.updateOne( + { + userId, + }, + { + userId, + $pull: { deviceToken }, + }, + { upsert: true, new: true } + ); + return newDeviceToken.deviceToken; + } catch (error) { + logger.error(error); + return new Array(); + } +}; + /** * 사용자들의 ObjectId의 배열이 주어졌을 때, 해당 사용자들의 모든 deviceToken을 하나의 Array로 반환합니다. * @param {Array} userIds - 사용자의 ObjectId로 이루어진 Array입니다. @@ -211,6 +261,8 @@ const unsubscribeUserFromTopic = async (userId, topic) => { }; module.exports = { + registerDeviceToken, + unregisterDeviceToken, getTokensOfUsers, sendMessageByToken, sendMessageByTokens, diff --git a/src/service/auth.js b/src/service/auth.js index 5f6da88c..53d9ab89 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -1,20 +1,23 @@ const security = require("../../security"); -const { userModel, deviceTokenModel } = require("../db/mongo"); +const { userModel } = require("../db/mongo"); +const { user: userPattern } = require("../db/patterns"); const { getLoginInfo, logout, login } = require("../auth/login"); + +const { + registerDeviceToken, + unregisterDeviceToken, +} = require("../modules/fcm"); +const logger = require("../modules/logger"); const { generateNickname, generateProfileImageUrl, getFullUsername, } = require("../modules/modifyProfile"); - const jwt = require("../modules/jwt"); const APP_URI_SCHEME = require("../../security").appUriScheme; -const { user: userPattern } = require("../db/patterns"); - // SPARCS SSO const Client = require("../auth/sparcsso"); -const logger = require("../modules/logger"); const client = new Client(security.sparcssso?.id, security.sparcssso?.key); const transUserData = (userData) => { @@ -157,9 +160,17 @@ const createNewTokenHandler = (req, res, userData) => { ); }; -const logoutHandler = (req, res) => { +const logoutHandler = async (req, res) => { try { - const { sid } = getLoginInfo(req); + const { id, sid } = getLoginInfo(req); + + // DB에서 deviceToken 레코드를 삭제합니다. + const deviceToken = req.session?.deviceToken; + if (deviceToken) { + const user = await userModel.findOne({ id }, "_id"); + await unregisterDeviceToken(user._id, deviceToken); + } + const redirectUrl = security.frontUrl + "/login"; const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); logout(req, res); @@ -171,31 +182,18 @@ const logoutHandler = (req, res) => { const registerDeviceTokenHandler = async (req, res) => { try { - const newDeviceToken = req.body.deviceToken; + const deviceToken = req.body.deviceToken; // 데이터베이스에 새 레코드를 추가합니다. - const user = await userModel.findOne( - { id: req.userId }, - "_id ongoingRoom doneRoom" - ); - const deviceToken = await deviceTokenModel.updateOne( - { - userId: user._id, - }, - { - userId: user._id, - $addToSet: { deviceToken: newDeviceToken }, - }, - { upsert: true, new: true } - ); - // 사용자의 모든 deviceToken들을 사용자가 참여중인 방들에 해당하는 topic에 구독시킵니다. - const ongoingRoom = user.ongoingRoom; - const doneRoom = user.doneRoom; - const roomIds = ongoingRoom - .concat(doneRoom) - .map((objectId) => objectId.toString()); + const user = await userModel.findOne({ id: req.userId }, "_id"); + + // DB에 deviceToken 레코드를 추가합니다. + const newDeviceToken = await registerDeviceToken(user._id, deviceToken); + + // 세션에 현재 사용자 기기의 deviceToken을 저장합니다. + req.session.deviceToken = deviceToken; return res.status(200).json({ - deviceToken: deviceToken.deviceToken, + deviceToken: newDeviceToken, }); } catch (e) { logger.error(e); diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index a2c7a65d..ea16ee6d 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -1,11 +1,15 @@ const security = require("../../security"); -const { userModel, deviceTokenModel } = require("../db/mongo"); -const { logout, login } = require("../auth/login"); +const { userModel } = require("../db/mongo"); +const { getLoginInfo, logout, login } = require("../auth/login"); + +const { + registerDeviceToken, + unregisterDeviceToken, +} = require("../modules/fcm"); const { generateNickname, generateProfileImageUrl, } = require("../modules/modifyProfile"); - const logger = require("../modules/logger"); const loginHtml = ` @@ -124,8 +128,17 @@ const sparcsssoHandler = (req, res) => { res.end(loginHtml); }; -const logoutHandler = (req, res) => { +const logoutHandler = async (req, res) => { try { + const { id } = getLoginInfo(req); + + // DB에서 deviceToken 레코드를 삭제합니다. + const deviceToken = req.session?.deviceToken; + if (deviceToken) { + const user = await userModel.findOne({ id }, "_id"); + await unregisterDeviceToken(user._id, deviceToken); + } + const ssoLogoutUrl = security.frontUrl + "/login"; logout(req, res); res.json({ ssoLogoutUrl }); @@ -136,31 +149,18 @@ const logoutHandler = (req, res) => { const registerDeviceTokenHandler = async (req, res) => { try { - const newDeviceToken = req.body.deviceToken; + const deviceToken = req.body.deviceToken; // 데이터베이스에 새 레코드를 추가합니다. - const user = await userModel.findOne( - { id: req.userId }, - "_id ongoingRoom doneRoom" - ); - const deviceToken = await deviceTokenModel.updateOne( - { - userId: user._id, - }, - { - userId: user._id, - $addToSet: { deviceToken: newDeviceToken }, - }, - { upsert: true, new: true } - ); - // 사용자의 모든 deviceToken들을 사용자가 참여중인 방들에 해당하는 topic에 구독시킵니다. - const ongoingRoom = user.ongoingRoom; - const doneRoom = user.doneRoom; - const roomIds = ongoingRoom - .concat(doneRoom) - .map((objectId) => objectId.toString()); + const user = await userModel.findOne({ id: req.userId }, "_id"); + + // DB에 deviceToken 레코드를 추가합니다. + const newDeviceToken = await registerDeviceToken(user._id, deviceToken); + + // 세션에 현재 사용자 기기의 deviceToken을 저장합니다. + req.session.deviceToken = deviceToken; return res.status(200).json({ - deviceToken: deviceToken.deviceToken, + deviceToken: newDeviceToken, }); } catch (e) { logger.error(e); From 8a53c770c1434540859d2368a8c4cbb74402e90d Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 01:33:02 +0900 Subject: [PATCH 105/308] Add: implement basic image notification --- src/route/chats.socket.js | 2 -- src/service/chats.js | 22 +++++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 92737dbd..90e9cbce 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -187,13 +187,11 @@ const ioListeners = (io, socket) => { // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. const room = await roomModel.findById(roomId, "name part"); - const topic = `room-${roomId}`; const urlOnClick = `/myroom/${roomId}`; const userIdsExceptMe = room.part .map((participant) => participant.user) .filter((userId) => userId !== myUser._id); const deviceTokens = await getTokensOfUsers(userIdsExceptMe); - logger.info(deviceTokens); await sendMessageByTokens( deviceTokens, room.name, diff --git a/src/service/chats.js b/src/service/chats.js index 96890c11..e732b5cf 100644 --- a/src/service/chats.js +++ b/src/service/chats.js @@ -1,6 +1,8 @@ -const { chatModel, userModel } = require("../db/mongo"); +const { chatModel, userModel, roomModel } = require("../db/mongo"); const awsS3 = require("../db/awsS3"); +const { getTokensOfUsers, sendMessageByTokens } = require("../modules/fcm"); + const uploadChatImgGetPUrlHandler = async (req, res) => { try { const type = req.body.type; @@ -95,6 +97,8 @@ const uploadChatImgDoneHandler = async (req, res) => { chatAfter.authorName = user.nickname; chatAfter.authorProfileUrl = user.profileImageUrl; + + // 방의 모든 사용자에게 이미지 수신 이벤트를 발생시킵니다. req.app .get("io") .to(`chatRoom-${chatAfter.roomId}`) @@ -102,6 +106,22 @@ const uploadChatImgDoneHandler = async (req, res) => { chat: chatAfter, }); + // 이미지 전송 알림을 전송합니다. + // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. + const room = await roomModel.findById(chatAfter.roomId, "name part"); + const urlOnClick = `/myroom/${chatAfter.roomId}`; + const userIdsExceptMe = room.part + .map((participant) => participant.user) + .filter((userId) => userId !== user._id); + const deviceTokens = await getTokensOfUsers(userIdsExceptMe); + await sendMessageByTokens( + deviceTokens, + room.name, + `${user.nickname}: Image`, + awsS3.getS3Url(`/profile-img/${user.profileImageUrl}`), + urlOnClick + ); + res.json({ result: true, }); From 8a8bcdbfed03e50ba7f822656d56f37c5a89427a Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 03:05:24 +0900 Subject: [PATCH 106/308] Refactor: replace io.to.emit by emitChatEvent --- src/modules/fcm.js | 21 +++++----- src/route/chats.socket.js | 84 ++++++++++++++++++++++----------------- src/service/chats.js | 46 ++------------------- 3 files changed, 63 insertions(+), 88 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index bed33d29..eafb86af 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -138,45 +138,48 @@ const sendNotificationByTopic = async (data, topic) => { /** * 주어진 token에 메시지 알림을 전송합니다. * @param {string} token - 메시지 알림을 받을 기기의 deviceToken입니다. + * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} content - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ -const sendMessageByToken = async (token, title, body, icon, url) => { +const sendMessageByToken = async (token, type, title, content, icon, url) => { url = url || "/myroom"; - const data = { title, body, icon, url }; + const data = { type, title, content, icon, url }; return await sendNotificationByToken(data, token); }; /** * 주어진 token들에 메시지 알림을 전송합니다. * @param {Array} tokens - 메시지 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. + * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} content - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 메시지 알림 전송에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ -const sendMessageByTokens = async (tokens, title, body, icon, url) => { +const sendMessageByTokens = async (tokens, type, title, content, icon, url) => { url = url || "/myroom"; - const data = { title, body, icon, url }; + const data = { type, title, content, icon, url }; return await sendNotificationByTokens(data, tokens); }; /** * 주어진 topic을 구독하고 있는 모든 기기에 메시지 알림을 전송합니다. * @param {string} topic - 메시지 알림을 보낼 기기들이 구독하고 있는 topic입니다. + * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} content - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ -const sendMessageByTopic = async (topic, title, body, icon, url) => { +const sendMessageByTopic = async (topic, type, title, content, icon, url) => { url = url || "/myroom"; - const data = { title, body, icon, url }; + const data = { type, title, content, icon, url }; return await sendNotificationByTopic(data, topic); }; diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 90e9cbce..ba6c6b01 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -24,38 +24,63 @@ const chatPopulateOption = [ const emitChatEvent = async (io, roomId, chat) => { try { // chat must contain type, content and authorId + // chat can contain time or not. if (!io || !roomId || !chat?.type || !chat?.content || !chat?.authorId) { throw new IllegalArgumentsException(); } - const author = await userModel.findById(chat.authorId); + const { type, content, authorId } = chat; + const time = chat?.time || Date.now(); + const author = await userModel.findById( + authorId, + "_id nickname profileImageUrl" + ); if (!author) { throw new IllegalArgumentsException(); } - chat.roomId = roomId; - chat.time = Date.now(); - - const chatDocument = new chatModel(chat); - await chatDocument.save(); - - chat.authorName = author.nickname; - chat.authorProfileUrl = author.profileImageUrl; - if (chat.type == "in" || chat.type == "out") { - const userIds = chat.content.split("|"); - chat.inOutNames = []; - for (const userId of userIds) { - const user = await userModel.findOne({ id: userId }); - if (!user) { - throw new IllegalArgumentsException(); - } - chat.inOutNames.push(user.nickname); - } - } - io.to(`chatRoom-${roomId}`).emit("chats-receive", { chat }); + const chatDocument = await chatModel.findOneAndUpdate( + { + type, + authorId, + roomId, + time, + }, + { + type, + authorId, + roomId, + time, + content, + isValid: true, + }, + { upsert: true, new: true } + ); + + logger.info(chatDocument); + + // 방의 모든 사용자에게 이미지 수신 이벤트를 발생시킵니다. + io.to(`chatRoom-${roomId}`).emit("chats-receive", { chatDocument }); + + // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. + const room = await roomModel.findById(roomId, "name part"); + const urlOnClick = `/myroom/${roomId}`; + const userIdsExceptAuthor = room.part + .map((participant) => participant.user) + .filter((userId) => userId !== authorId); + const deviceTokens = await getTokensOfUsers(userIdsExceptAuthor); + await sendMessageByTokens( + deviceTokens, + type, + room.name, + `${author.nickname}: ${content}`, + getS3Url(`/profile-img/${author.profileImageUrl}`), + urlOnClick + ); + return true; } catch (err) { logger.error(err); - return; + return false; } }; @@ -184,21 +209,6 @@ const ioListeners = (io, socket) => { authorId: myUser._id, }); io.to(socket.id).emit("chats-send", { done: true }); - - // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. - const room = await roomModel.findById(roomId, "name part"); - const urlOnClick = `/myroom/${roomId}`; - const userIdsExceptMe = room.part - .map((participant) => participant.user) - .filter((userId) => userId !== myUser._id); - const deviceTokens = await getTokensOfUsers(userIdsExceptMe); - await sendMessageByTokens( - deviceTokens, - room.name, - `${myUser.nickname}: ${chatMessage.content}`, - getS3Url(`/profile-img/${myUser.profileImageUrl}`), - urlOnClick - ); } catch (err) { logger.error(err); io.to(socket.id).emit("chats-send", { err: true }); diff --git a/src/service/chats.js b/src/service/chats.js index e732b5cf..1b1fb012 100644 --- a/src/service/chats.js +++ b/src/service/chats.js @@ -2,6 +2,7 @@ const { chatModel, userModel, roomModel } = require("../db/mongo"); const awsS3 = require("../db/awsS3"); const { getTokensOfUsers, sendMessageByTokens } = require("../modules/fcm"); +const { emitChatEvent } = require("../route/chats.socket"); const uploadChatImgGetPUrlHandler = async (req, res) => { try { @@ -52,7 +53,7 @@ const uploadChatImgDoneHandler = async (req, res) => { { id: req.userId }, "_id nickname profileImageUrl" ); - const chat = await chatModel.findById(req.body.id); + const chat = await chatModel.findById(req.body.id).lean(); if (!user) { return res .status(500) @@ -79,48 +80,9 @@ const uploadChatImgDoneHandler = async (req, res) => { .status(500) .send("Chat/uploadChatImg/getPUrl : internal server error"); } - const chatAfter = await chatModel - .findOneAndUpdate( - { _id: chat._id }, - { - isValid: true, - content: chat._id.toString(), - }, - { new: true } - ) - .lean(); - if (!chatAfter) { - return res - .status(500) - .send("User/editProfileImg/done : internal server error"); - } - - chatAfter.authorName = user.nickname; - chatAfter.authorProfileUrl = user.profileImageUrl; - - // 방의 모든 사용자에게 이미지 수신 이벤트를 발생시킵니다. - req.app - .get("io") - .to(`chatRoom-${chatAfter.roomId}`) - .emit("chats-receive", { - chat: chatAfter, - }); - // 이미지 전송 알림을 전송합니다. - // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. - const room = await roomModel.findById(chatAfter.roomId, "name part"); - const urlOnClick = `/myroom/${chatAfter.roomId}`; - const userIdsExceptMe = room.part - .map((participant) => participant.user) - .filter((userId) => userId !== user._id); - const deviceTokens = await getTokensOfUsers(userIdsExceptMe); - await sendMessageByTokens( - deviceTokens, - room.name, - `${user.nickname}: Image`, - awsS3.getS3Url(`/profile-img/${user.profileImageUrl}`), - urlOnClick - ); + chat.content = chat._id; + emitChatEvent(req.app.get("io"), chat.roomId, chat); res.json({ result: true, From 595b04f471c1314dfbfb2dc002f08897ece92d9c Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 03:38:44 +0900 Subject: [PATCH 107/308] Docs: add comments to chat handlers --- src/route/chats.socket.js | 50 ++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index ba6c6b01..86bb315d 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -5,6 +5,16 @@ const validator = require("validator"); const { getTokensOfUsers, sendMessageByTokens } = require("../modules/fcm"); const logger = require("../modules/logger"); +/** @constant {{path: string, select: string}[]} + * 쿼리를 통해 얻은 Chat Document를 populate할 설정값을 정의합니다. + */ +const chatPopulateOption = [ + { path: "authorId", select: "_id nickname profileImageUrl" }, +]; + +/** + * emitChatEvent의 필수 파라미터가 주어지지 않은 경우 발생하는 예외를 정의하는 클래스입니다. + */ class IllegalArgumentsException { constructor() { this.toString = () => { @@ -13,14 +23,37 @@ class IllegalArgumentsException { } } -/** @constant {{path: string, select: string}[]} - * 쿼리를 통해 얻은 Chat Document를 populate할 설정값을 정의합니다. +/** + * 채팅 타입과 작성자의 닉네임, 본문을 받아 채팅 타입에 따라 다르게 FCM 알림으로 보낼 content를 생성합니다. + * @summary emitChatEvent에서만 사용됩니다. + * @param {string} type - 채팅 메시지의 유형입니다. "text" | "s3img" | "in" | "out" 입니다. + * @param {string} nickname - 작성자의 닉네임입니다. + * @param {string} content - 메시지 본문입니다. + * @return {string} FCM 알림으로 보낼 content입니다. */ -const chatPopulateOption = [ - { path: "authorId", select: "_id nickname profileImageUrl" }, -]; +const getNotificationContent = (type, nickname, content) => { + if (type === "text") { + return `${nickname}: ${content}`; + } else if (type === "s3img") { + return `${nickname}: Image`; + } else { + // type이 "in"이거나 "out"인 경우 + return `${nickname}: ${type}`; + } +}; -// express 라우터에서 채팅 이벤트를 보낼 수 있게 함수를 분리했습니다. +/** + * 채팅을 전송하고 채팅 알림을 발생시킵니다. + * @summary express 라우터에서 채팅 이벤트를 보낼 수 있게 함수를 분리했습니다. + * @param {Server} io - Socket.io 서버 인스턴스입니다. req.app.get("io")를 통해 접근할 수 있습니다. + * @param {string} roomId - 채팅 및 채팅 알림을 보낼 방의 ObjectId입니다. + * @param {Object} chat - 채팅 메시지 내용입니다. + * @param {string} chat.type - 채팅 메시지의 유형입니다. "text" | "s3img" | "in" | "out" 입니다. + * @param {string} chat.content - 채팅 메시지의 본문입니다. chat.type이 "s3img"인 경우에는 채팅의 objectId입니다. chat.type이 "in"이거나 "out"인 경우 입퇴장한 사용자의 id(!==ObjectId)입니다. + * @param {string} chat.authorId - 채팅을 보낸 사용자의 ObjectId입니다. + * @param {Date?} chat.time - optional. 채팅 메시지 전송 시각입니다. + * @return {Promise} 채팅 및 알림 전송에 성공하면 true, 중간에 오류가 발생하면 false를 반환합니다. + */ const emitChatEvent = async (io, roomId, chat) => { try { // chat must contain type, content and authorId @@ -69,11 +102,12 @@ const emitChatEvent = async (io, roomId, chat) => { .map((participant) => participant.user) .filter((userId) => userId !== authorId); const deviceTokens = await getTokensOfUsers(userIdsExceptAuthor); + await sendMessageByTokens( deviceTokens, type, room.name, - `${author.nickname}: ${content}`, + getNotificationContent(type, author.nickname, content), getS3Url(`/profile-img/${author.profileImageUrl}`), urlOnClick ); @@ -87,7 +121,7 @@ const emitChatEvent = async (io, roomId, chat) => { /** * Chat Object의 array가 주어졌을 때 클라이언트에서 처리하기 편한 형태로 Chat Object를 가공합니다. * @param {[Object]} chats - Chats Document에 lean과 populate(chatPopulateOption)을 차례로 적용한 Chat Object의 배열입니다. - * @return {Promise} {type: String, authorId: String, authorName: String, authorProfileUrl: String, content: string, time: Date}로 이루어진 chat 객체의 배열입니다. + * @return {Promise} {type: String, authorId: String, authorName: String, authorProfileUrl: String, content: string, time: Date}로 이루어진 chat 객체의 배열입니다. */ const transformChatsForRoom = async (chats) => { const chatsToSend = []; From 21d8cccec65c39c0841c94ac0e9cae6f7d8f3830 Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 04:18:36 +0900 Subject: [PATCH 108/308] Refactor: change argument name of notification handlers --- src/modules/fcm.js | 18 ++++++------ src/route/chats.socket.js | 61 ++++++++++++++++++++------------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index eafb86af..fbc24acd 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -140,14 +140,14 @@ const sendNotificationByTopic = async (data, topic) => { * @param {string} token - 메시지 알림을 받을 기기의 deviceToken입니다. * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} content - 보낼 메시지의 본문입니다. + * @param {string} body - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ -const sendMessageByToken = async (token, type, title, content, icon, url) => { +const sendMessageByToken = async (token, type, title, body, icon, url) => { url = url || "/myroom"; - const data = { type, title, content, icon, url }; + const data = { type, title, body, icon, url }; return await sendNotificationByToken(data, token); }; @@ -156,14 +156,14 @@ const sendMessageByToken = async (token, type, title, content, icon, url) => { * @param {Array} tokens - 메시지 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} content - 보낼 메시지의 본문입니다. + * @param {string} body - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 메시지 알림 전송에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ -const sendMessageByTokens = async (tokens, type, title, content, icon, url) => { +const sendMessageByTokens = async (tokens, type, title, body, icon, url) => { url = url || "/myroom"; - const data = { type, title, content, icon, url }; + const data = { type, title, body, icon, url }; return await sendNotificationByTokens(data, tokens); }; @@ -172,14 +172,14 @@ const sendMessageByTokens = async (tokens, type, title, content, icon, url) => { * @param {string} topic - 메시지 알림을 보낼 기기들이 구독하고 있는 topic입니다. * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} content - 보낼 메시지의 본문입니다. + * @param {string} body - 보낼 메시지의 본문입니다. * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ -const sendMessageByTopic = async (topic, type, title, content, icon, url) => { +const sendMessageByTopic = async (topic, type, title, body, icon, url) => { url = url || "/myroom"; - const data = { type, title, content, icon, url }; + const data = { type, title, body, icon, url }; return await sendNotificationByTopic(data, topic); }; diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 86bb315d..f32bea70 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -23,22 +23,16 @@ class IllegalArgumentsException { } } -/** - * 채팅 타입과 작성자의 닉네임, 본문을 받아 채팅 타입에 따라 다르게 FCM 알림으로 보낼 content를 생성합니다. - * @summary emitChatEvent에서만 사용됩니다. - * @param {string} type - 채팅 메시지의 유형입니다. "text" | "s3img" | "in" | "out" 입니다. - * @param {string} nickname - 작성자의 닉네임입니다. - * @param {string} content - 메시지 본문입니다. - * @return {string} FCM 알림으로 보낼 content입니다. - */ const getNotificationContent = (type, nickname, content) => { if (type === "text") { + // type이 "text"인 경우, nickname과 content를 합쳐서 반환 return `${nickname}: ${content}`; } else if (type === "s3img") { - return `${nickname}: Image`; + // type이 "s3img"이거나 "in", "out"인 경우에는 nickname만 반환 + return nickname; } else { // type이 "in"이거나 "out"인 경우 - return `${nickname}: ${type}`; + return nickname; } }; @@ -72,30 +66,36 @@ const emitChatEvent = async (io, roomId, chat) => { throw new IllegalArgumentsException(); } - const chatDocument = await chatModel.findOneAndUpdate( - { - type, - authorId, - roomId, - time, - }, - { - type, - authorId, - roomId, - time, - content, - isValid: true, - }, - { upsert: true, new: true } - ); + const chatDocument = await chatModel + .findOneAndUpdate( + { + type, + authorId, + roomId, + time, + }, + { + type, + authorId, + roomId, + time, + content, + isValid: true, + }, + { upsert: true, new: true } + ) + .lean(); logger.info(chatDocument); // 방의 모든 사용자에게 이미지 수신 이벤트를 발생시킵니다. - io.to(`chatRoom-${roomId}`).emit("chats-receive", { chatDocument }); + io.to(`chatRoom-${roomId}`).emit("chats-receive", { chat: chatDocument }); + + // FCM 알림으로 보내는 content는 채팅 type에 따라 달라집니다. + // type이 text인 경우 `${nickname}: ${content}`를, 아닌 경우 `${nickname}`를 보냅니다. + const body = + type === "text" ? `${author.nickname}: ${content}` : author.nickname; - // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. const room = await roomModel.findById(roomId, "name part"); const urlOnClick = `/myroom/${roomId}`; const userIdsExceptAuthor = room.part @@ -103,11 +103,12 @@ const emitChatEvent = async (io, roomId, chat) => { .filter((userId) => userId !== authorId); const deviceTokens = await getTokensOfUsers(userIdsExceptAuthor); + // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. await sendMessageByTokens( deviceTokens, type, room.name, - getNotificationContent(type, author.nickname, content), + body, getS3Url(`/profile-img/${author.profileImageUrl}`), urlOnClick ); From 1ff0cea4bb510111bf8d51d2e03e759678a49431 Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 04:41:34 +0900 Subject: [PATCH 109/308] Fix: Add missing nickname and profileImageUrl in chat --- src/route/chats.socket.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index f32bea70..6aafbca2 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -58,11 +58,11 @@ const emitChatEvent = async (io, roomId, chat) => { const { type, content, authorId } = chat; const time = chat?.time || Date.now(); - const author = await userModel.findById( + const { nickname, profileImageUrl } = await userModel.findById( authorId, - "_id nickname profileImageUrl" + "nickname profileImageUrl" ); - if (!author) { + if (!nickname) { throw new IllegalArgumentsException(); } @@ -85,16 +85,15 @@ const emitChatEvent = async (io, roomId, chat) => { { upsert: true, new: true } ) .lean(); - - logger.info(chatDocument); + chatDocument.authorName = nickname; + chatDocument.authorProfileUrl = profileImageUrl; // 방의 모든 사용자에게 이미지 수신 이벤트를 발생시킵니다. io.to(`chatRoom-${roomId}`).emit("chats-receive", { chat: chatDocument }); // FCM 알림으로 보내는 content는 채팅 type에 따라 달라집니다. // type이 text인 경우 `${nickname}: ${content}`를, 아닌 경우 `${nickname}`를 보냅니다. - const body = - type === "text" ? `${author.nickname}: ${content}` : author.nickname; + const body = type === "text" ? `${nickname}: ${content}` : nickname; const room = await roomModel.findById(roomId, "name part"); const urlOnClick = `/myroom/${roomId}`; @@ -109,7 +108,7 @@ const emitChatEvent = async (io, roomId, chat) => { type, room.name, body, - getS3Url(`/profile-img/${author.profileImageUrl}`), + getS3Url(`/profile-img/${profileImageUrl}`), urlOnClick ); return true; From 6a1f6a696ec7afce33cae4f59b3daebe5a101cb0 Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 05:15:24 +0900 Subject: [PATCH 110/308] Refactor: send notification using "send request" --- src/modules/fcm.js | 128 ++++++++++++++++++-------------------- src/route/chats.socket.js | 26 ++++---- 2 files changed, 75 insertions(+), 79 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index fbc24acd..e5ce740b 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -73,16 +73,28 @@ const getTokensOfUsers = async (userIds) => { }; /** - * 주어진 token에 해당하는 기기에 data를 전송합니다. - * @param {string} token - 알림을 받을 기기의 deviceToken입니다. - * @param {Object} data - 기기에 전송할 key-value pair입니다. 모든 value는 string 타입이어야 합니다. - * @return {Promise} data 전송에 성공했으면 true, 아니면 false를 반환합니다. + * 주어진 token에 메시지 알림을 전송합니다. + * @param {string} token - 메시지 알림을 받을 기기의 deviceToken입니다. + * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. + * @param {string} title - 보낼 메시지의 제목입니다. + * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @param {string?} link - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. + * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ -const sendNotificationByToken = async (data, token) => { +const sendMessageByToken = async (token, type, title, body, icon, link) => { try { const message = { - data, token, + notification: { + title, + body, + }, + webpush: { + fcm_options: { + link: link || "/myroom", + }, + }, }; await getMessaging().send(message); logger.info(`Notification sent to token ${token}`); @@ -94,19 +106,35 @@ const sendNotificationByToken = async (data, token) => { }; /** - * 주어진 token들에 해당하는 기기들에 data를 전송합니다. - * @param {Array} tokens - 알림을 받을 기기들의 deviceToken들입니다. - * @param {Object} data - 기기에 전송할 key-value pair입니다. 모든 value는 string 타입이어야 합니다. - * @return {Promise} data 전송에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. + * 주어진 token들에 메시지 알림을 전송합니다. + * @param {Array} tokens - 메시지 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. + * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. + * @param {string} title - 보낼 메시지의 제목입니다. + * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @param {string?} link - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. + * @return {Promise} 메시지 알림 전송에 실패한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ -const sendNotificationByTokens = async (data, tokens) => { +const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { + if (tokens.length === 0) return -1; try { - if (tokens.length === 0) return -1; const message = { - data, tokens, + notification: { + title, + body, + }, + webpush: { + notification: { + icon, + }, + fcm_options: { + link: link || "/myroom", + }, + }, }; const { failureCount } = await getMessaging().sendMulticast(message); + logger.info(`Notification sent failed for ${failureCount} devices`); return failureCount; } catch (error) { logger.error(error); @@ -115,19 +143,31 @@ const sendNotificationByTokens = async (data, tokens) => { }; /** - * 주어진 topic을 구독하고 있는 모든 기기에 data를 전송합니다. - * @param {string} token - data를 보낼 기기들이 구독하고 있는 topic입니다. - * @param {Object} data - 기기에 전송할 key-value pair입니다. 모든 value는 string 타입이어야 합니다. - * @return {Promise} data 전송에 성공했으면 true, 아니면 false를 반환합니다. + * 주어진 topic을 구독하고 있는 모든 기기에 메시지 알림을 전송합니다. + * @param {string} topic - 메시지 알림을 보낼 기기들이 구독하고 있는 topic입니다. + * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. + * @param {string} title - 보낼 메시지의 제목입니다. + * @param {string} body - 보낼 메시지의 본문입니다. + * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @param {string?} link - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. + * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ -const sendNotificationByTopic = async (data, topic) => { +const sendMessageByTopic = async (topic, type, title, body, icon, link) => { try { const message = { - data, topic, + notification: { + title, + body, + }, + webpush: { + fcm_options: { + link: link || "/myroom", + }, + }, }; await getMessaging().send(message); - logger.info(`Notification sent to topic ${topic}`); + logger.info(`Notification sent to token ${topic}`); return true; } catch (error) { logger.error(error); @@ -135,54 +175,6 @@ const sendNotificationByTopic = async (data, topic) => { } }; -/** - * 주어진 token에 메시지 알림을 전송합니다. - * @param {string} token - 메시지 알림을 받을 기기의 deviceToken입니다. - * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. - * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} body - 보낼 메시지의 본문입니다. - * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. - * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. - * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. - */ -const sendMessageByToken = async (token, type, title, body, icon, url) => { - url = url || "/myroom"; - const data = { type, title, body, icon, url }; - return await sendNotificationByToken(data, token); -}; - -/** - * 주어진 token들에 메시지 알림을 전송합니다. - * @param {Array} tokens - 메시지 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. - * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. - * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} body - 보낼 메시지의 본문입니다. - * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. - * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. - * @return {Promise} 메시지 알림 전송에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. - */ -const sendMessageByTokens = async (tokens, type, title, body, icon, url) => { - url = url || "/myroom"; - const data = { type, title, body, icon, url }; - return await sendNotificationByTokens(data, tokens); -}; - -/** - * 주어진 topic을 구독하고 있는 모든 기기에 메시지 알림을 전송합니다. - * @param {string} topic - 메시지 알림을 보낼 기기들이 구독하고 있는 topic입니다. - * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. - * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} body - 보낼 메시지의 본문입니다. - * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. - * @param {string?} url - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. - * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. - */ -const sendMessageByTopic = async (topic, type, title, body, icon, url) => { - url = url || "/myroom"; - const data = { type, title, body, icon, url }; - return await sendNotificationByTopic(data, topic); -}; - /** * 주어진 사용자를 특정한 topic에 구독시킵니다. * @param {string} userId - topic을 구독할 사용자의 ObjectId입니다. diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 6aafbca2..49c3be59 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -23,16 +23,24 @@ class IllegalArgumentsException { } } -const getNotificationContent = (type, nickname, content) => { +// FCM 알림으로 보내는 content는 채팅 type에 따라 달라집니다. +// type이 text인 경우 `${nickname}: ${content}`를, 아닌 경우 `${nickname}`를 보냅니다. +const getMessageBody = (type, nickname, content) => { + // TODO: 채팅 메시지 유형에 따라 Body를 다르게 표시합니다. if (type === "text") { - // type이 "text"인 경우, nickname과 content를 합쳐서 반환 + // 채팅 메시지 유형이 텍스트인 경우 본문은 "${nickname}: ${content}"가 됩니다. return `${nickname}: ${content}`; } else if (type === "s3img") { - // type이 "s3img"이거나 "in", "out"인 경우에는 nickname만 반환 - return nickname; + // 채팅 유형이 이미지인 경우 본문은 "${nickname} 님이 이미지를 전송하였습니다"가 됩니다. + // TODO: 사용자 언어를 가져올 수 있으면 개선할 수 있다고 생각합니다. + const suffix = " 님이 이미지를 전송하였습니다."; + return `${nickname} ${suffix}`; } else { - // type이 "in"이거나 "out"인 경우 - return nickname; + // 채팅 메시지 type이 "in"이거나 "out"인 경우 본문은 "${nickname} 님이 입장하였습니다" 또는 "${nickname} 님이 퇴장하였습니다"가 됩니다. + // TODO: 사용자 언어를 가져올 수 있으면 개선할 수 있다고 생각합니다. + const suffix = + type === "in" ? " 님이 입장하였습니다" : "님이 퇴장하였습니다"; + return `${nickname} ${suffix}`; } }; @@ -91,10 +99,6 @@ const emitChatEvent = async (io, roomId, chat) => { // 방의 모든 사용자에게 이미지 수신 이벤트를 발생시킵니다. io.to(`chatRoom-${roomId}`).emit("chats-receive", { chat: chatDocument }); - // FCM 알림으로 보내는 content는 채팅 type에 따라 달라집니다. - // type이 text인 경우 `${nickname}: ${content}`를, 아닌 경우 `${nickname}`를 보냅니다. - const body = type === "text" ? `${nickname}: ${content}` : nickname; - const room = await roomModel.findById(roomId, "name part"); const urlOnClick = `/myroom/${roomId}`; const userIdsExceptAuthor = room.part @@ -107,7 +111,7 @@ const emitChatEvent = async (io, roomId, chat) => { deviceTokens, type, room.name, - body, + getMessageBody(type, nickname, content), getS3Url(`/profile-img/${profileImageUrl}`), urlOnClick ); From 0e7a323aba9221b91a87dc99a6ce00f498e0cc61 Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 05:17:29 +0900 Subject: [PATCH 111/308] Fix: add profile image for chatting --- src/modules/fcm.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index e5ce740b..81169a49 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -91,6 +91,9 @@ const sendMessageByToken = async (token, type, title, body, icon, link) => { body, }, webpush: { + notification: { + icon, + }, fcm_options: { link: link || "/myroom", }, @@ -161,6 +164,9 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { body, }, webpush: { + notification: { + icon, + }, fcm_options: { link: link || "/myroom", }, From cc54c7efdf315cde94a2bdaeda00b9e3d7a15635 Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 05:20:27 +0900 Subject: [PATCH 112/308] Fix: change default link for chat notification --- src/modules/fcm.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 81169a49..707c95d5 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -95,7 +95,7 @@ const sendMessageByToken = async (token, type, title, body, icon, link) => { icon, }, fcm_options: { - link: link || "/myroom", + link: link || "/", }, }, }; @@ -132,7 +132,7 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { icon, }, fcm_options: { - link: link || "/myroom", + link: link || "/", }, }, }; @@ -168,7 +168,7 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { icon, }, fcm_options: { - link: link || "/myroom", + link: link || "/", }, }, }; From 47dffd3661e4accaa57cf9e52d04f7a89ee6564f Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 05:27:05 +0900 Subject: [PATCH 113/308] Refactor: change deviceTokenSchema from deviceToken to deviceTokens --- src/db/mongo.js | 2 +- src/modules/fcm.js | 32 ++++++++++++++++---------------- src/service/auth.mobile.js | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/db/mongo.js b/src/db/mongo.js index da3f3c52..46cdb51b 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -43,7 +43,7 @@ const deviceTokenSchema = Schema({ required: true, unique: true, }, - deviceToken: [{ type: String, required: true }], + deviceTokens: [{ type: String, required: true }], registeredAt: { type: Date, default: () => Date.now(), diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 707c95d5..09b09f16 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -10,17 +10,17 @@ const logger = require("../modules/logger"); */ const registerDeviceToken = async (userId, deviceToken) => { try { - const newDeviceToken = await deviceTokenModel.updateOne( + const { deviceTokens } = await deviceTokenModel.updateOne( { userId, }, { userId, - $addToSet: { deviceToken }, + $addToSet: { deviceTokens: deviceToken }, }, { upsert: true, new: true } ); - return newDeviceToken.deviceToken; + return deviceTokens; } catch (error) { logger.error(error); return new Array(); @@ -35,17 +35,17 @@ const registerDeviceToken = async (userId, deviceToken) => { */ const unregisterDeviceToken = async (userId, deviceToken) => { try { - const newDeviceToken = await deviceTokenModel.updateOne( + const { deviceTokens } = await deviceTokenModel.updateOne( { userId, }, { userId, - $pull: { deviceToken }, + $pull: { deviceTokens: deviceToken }, }, { upsert: true, new: true } ); - return newDeviceToken.deviceToken; + return deviceTokens; } catch (error) { logger.error(error); return new Array(); @@ -58,15 +58,15 @@ const unregisterDeviceToken = async (userId, deviceToken) => { * @return {Promise>} deviceToken의 Array를 반환합니다. 오류가 발생하면 빈 배열을 반환합니다. */ const getTokensOfUsers = async (userIds) => { - const deviceTokenOfUsers = await Promise.all( + const deviceTokensOfUsers = await Promise.all( userIds.map(async (userId) => { - const deviceToken = await deviceTokenModel.findOne({ + const { deviceTokens } = await deviceTokenModel.findOne({ userId, }); - return deviceToken.deviceToken; + return deviceTokens; }) ); - return deviceTokenOfUsers.reduce( + return deviceTokensOfUsers.reduce( (arrayA, arrayB) => arrayA.concat(arrayB), new Array() ); @@ -189,17 +189,17 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { */ const subscribeUserToTopic = async (userId, topic) => { try { - const { deviceToken } = await deviceTokenModel.findOne({ + const { deviceTokens } = await deviceTokenModel.findOne({ userId, }); const { successCount } = await getMessaging().subscribeToTopic( - deviceToken, + deviceTokens, topic ); // 데이터베이스에 해당 토큰에 대한 토픽 구독 레코드를 추가합니다. await Promise.all( - deviceToken.map(async (token) => { + deviceTokens.map(async (token) => { return await topicSubscriptionModel.updateOne( { deviceToken: token, @@ -233,17 +233,17 @@ const subscribeUserToTopic = async (userId, topic) => { */ const unsubscribeUserFromTopic = async (userId, topic) => { try { - const { deviceToken } = await deviceTokenModel.findOne({ + const { deviceTokens } = await deviceTokenModel.findOne({ userId, }); const { successCount } = await getMessaging().unsubscribeFromTopic( - deviceToken, + deviceTokens, topic ); // 데이터베이스에서 해당 토큰에 대한 토픽 구독 레코드를 삭제합니다. await Promise.all( - deviceToken.map(async (token) => { + deviceTokens.map(async (token) => { return await topicSubscriptionModel.deleteOne({ deviceToken: token, topic: topic, diff --git a/src/service/auth.mobile.js b/src/service/auth.mobile.js index 0d6f020f..ae4ad656 100644 --- a/src/service/auth.mobile.js +++ b/src/service/auth.mobile.js @@ -99,7 +99,7 @@ const registerDeviceTokenHandler = async (req, res) => { }, { userId: accessTokenStatus.id, - $addToSet: { deviceToken: deviceToken }, + $addToSet: { deviceTokens: deviceToken }, }, { upsert: true, new: true } ); @@ -128,7 +128,7 @@ const removeDeviceTokenHandler = async (req, res) => { { userId: accessTokenStatus.id, }, - { userId: accessTokenStatus.id, $pull: { deviceToken: deviceToken } }, + { userId: accessTokenStatus.id, $pull: { deviceTokens: deviceToken } }, { upsert: true, new: true } ); res.status(200).send("success"); From de4b78a92f363200643ec4f3703c35c23db06d3e Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 05:53:46 +0900 Subject: [PATCH 114/308] Fix: remove using deviceToken in TDZ --- src/modules/fcm.js | 61 ++++++++------------------------------- src/route/chats.socket.js | 2 +- src/service/chats.js | 1 - 3 files changed, 13 insertions(+), 51 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 09b09f16..6faf5c9b 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -10,7 +10,7 @@ const logger = require("../modules/logger"); */ const registerDeviceToken = async (userId, deviceToken) => { try { - const { deviceTokens } = await deviceTokenModel.updateOne( + const newDeviceToken = await deviceTokenModel.updateOne( { userId, }, @@ -20,7 +20,7 @@ const registerDeviceToken = async (userId, deviceToken) => { }, { upsert: true, new: true } ); - return deviceTokens; + return newDeviceToken.deviceTokens; } catch (error) { logger.error(error); return new Array(); @@ -35,7 +35,7 @@ const registerDeviceToken = async (userId, deviceToken) => { */ const unregisterDeviceToken = async (userId, deviceToken) => { try { - const { deviceTokens } = await deviceTokenModel.updateOne( + const newDeviceToken = await deviceTokenModel.updateOne( { userId, }, @@ -45,7 +45,7 @@ const unregisterDeviceToken = async (userId, deviceToken) => { }, { upsert: true, new: true } ); - return deviceTokens; + return newDeviceToken.deviceTokens; } catch (error) { logger.error(error); return new Array(); @@ -60,10 +60,10 @@ const unregisterDeviceToken = async (userId, deviceToken) => { const getTokensOfUsers = async (userIds) => { const deviceTokensOfUsers = await Promise.all( userIds.map(async (userId) => { - const { deviceTokens } = await deviceTokenModel.findOne({ + const deviceToken = await deviceTokenModel.findOne({ userId, }); - return deviceTokens; + return deviceToken.deviceTokens; }) ); return deviceTokensOfUsers.reduce( @@ -72,42 +72,6 @@ const getTokensOfUsers = async (userIds) => { ); }; -/** - * 주어진 token에 메시지 알림을 전송합니다. - * @param {string} token - 메시지 알림을 받을 기기의 deviceToken입니다. - * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. - * @param {string} title - 보낼 메시지의 제목입니다. - * @param {string} body - 보낼 메시지의 본문입니다. - * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. - * @param {string?} link - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. - * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. - */ -const sendMessageByToken = async (token, type, title, body, icon, link) => { - try { - const message = { - token, - notification: { - title, - body, - }, - webpush: { - notification: { - icon, - }, - fcm_options: { - link: link || "/", - }, - }, - }; - await getMessaging().send(message); - logger.info(`Notification sent to token ${token}`); - return true; - } catch (error) { - logger.error(error); - return false; - } -}; - /** * 주어진 token들에 메시지 알림을 전송합니다. * @param {Array} tokens - 메시지 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. @@ -189,17 +153,17 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { */ const subscribeUserToTopic = async (userId, topic) => { try { - const { deviceTokens } = await deviceTokenModel.findOne({ + const deviceToken = await deviceTokenModel.findOne({ userId, }); const { successCount } = await getMessaging().subscribeToTopic( - deviceTokens, + deviceToken.deviceTokens, topic ); // 데이터베이스에 해당 토큰에 대한 토픽 구독 레코드를 추가합니다. await Promise.all( - deviceTokens.map(async (token) => { + deviceToken.deviceTokens.map(async (token) => { return await topicSubscriptionModel.updateOne( { deviceToken: token, @@ -233,17 +197,17 @@ const subscribeUserToTopic = async (userId, topic) => { */ const unsubscribeUserFromTopic = async (userId, topic) => { try { - const { deviceTokens } = await deviceTokenModel.findOne({ + const deviceToken = await deviceTokenModel.findOne({ userId, }); const { successCount } = await getMessaging().unsubscribeFromTopic( - deviceTokens, + deviceToken.deviceTokens, topic ); // 데이터베이스에서 해당 토큰에 대한 토픽 구독 레코드를 삭제합니다. await Promise.all( - deviceTokens.map(async (token) => { + deviceToken.deviceTokens.map(async (token) => { return await topicSubscriptionModel.deleteOne({ deviceToken: token, topic: topic, @@ -265,7 +229,6 @@ module.exports = { registerDeviceToken, unregisterDeviceToken, getTokensOfUsers, - sendMessageByToken, sendMessageByTokens, sendMessageByTopic, subscribeUserToTopic, diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 49c3be59..ce939ce4 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -103,7 +103,7 @@ const emitChatEvent = async (io, roomId, chat) => { const urlOnClick = `/myroom/${roomId}`; const userIdsExceptAuthor = room.part .map((participant) => participant.user) - .filter((userId) => userId !== authorId); + .filter((userId) => userId.toString() !== authorId.toString()); const deviceTokens = await getTokensOfUsers(userIdsExceptAuthor); // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. diff --git a/src/service/chats.js b/src/service/chats.js index 1b1fb012..b8022133 100644 --- a/src/service/chats.js +++ b/src/service/chats.js @@ -1,7 +1,6 @@ const { chatModel, userModel, roomModel } = require("../db/mongo"); const awsS3 = require("../db/awsS3"); -const { getTokensOfUsers, sendMessageByTokens } = require("../modules/fcm"); const { emitChatEvent } = require("../route/chats.socket"); const uploadChatImgGetPUrlHandler = async (req, res) => { From 97a42abaf9d6d8844591a849155c2eb0b88315be Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 07:12:41 +0900 Subject: [PATCH 115/308] Fix: cover case when user have no devicetoken --- src/modules/fcm.js | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 6faf5c9b..0f80dd62 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -63,7 +63,7 @@ const getTokensOfUsers = async (userIds) => { const deviceToken = await deviceTokenModel.findOne({ userId, }); - return deviceToken.deviceTokens; + return deviceToken?.deviceTokens || new Array(); }) ); return deviceTokensOfUsers.reduce( @@ -149,14 +149,19 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { * 주어진 사용자를 특정한 topic에 구독시킵니다. * @param {string} userId - topic을 구독할 사용자의 ObjectId입니다. * @param {string} topic - 구독할 topic입니다. - * @return {Promise} 토픽 구독에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. + * @return {Promise} 토픽 구독에 실패한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ const subscribeUserToTopic = async (userId, topic) => { try { const deviceToken = await deviceTokenModel.findOne({ userId, }); - const { successCount } = await getMessaging().subscribeToTopic( + // deviceToken이 존재하지 않는 경우, -1을 반환합니다. + if (!deviceToken?.deviceTokens || deviceToken.deviceTokens.length === 0) { + return -1; + } + + const { failureCount } = await getMessaging().subscribeToTopic( deviceToken.deviceTokens, topic ); @@ -181,8 +186,10 @@ const subscribeUserToTopic = async (userId, topic) => { }) ); - logger.info(`${userId}'s ${successCount} token(s) subscribed to ${topic}`); - return successCount; + logger.info( + `${userId}'s ${failureCount} token(s) were not subscribed to ${topic}` + ); + return failureCount; } catch (error) { logger.error(error); return -1; @@ -193,14 +200,19 @@ const subscribeUserToTopic = async (userId, topic) => { * 주어진 사용자를 특정한 topic으로부터 구독 해제시킵니다. * @param {string} userId - topic을 구독 해제할 사용자의 id입니다. * @param {string} topic - 구독을 해제할 topic입니다. - * @return {Promise} 토픽 구독 해제에 성공한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. + * @return {Promise} 토픽 구독 해제에 실패한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ const unsubscribeUserFromTopic = async (userId, topic) => { try { const deviceToken = await deviceTokenModel.findOne({ userId, }); - const { successCount } = await getMessaging().unsubscribeFromTopic( + // deviceToken이 존재하지 않는 경우, -1을 반환합니다. + if (!deviceToken?.deviceTokens || deviceToken.deviceTokens.length === 0) { + return -1; + } + + const { failureCount } = await getMessaging().unsubscribeFromTopic( deviceToken.deviceTokens, topic ); @@ -216,9 +228,9 @@ const unsubscribeUserFromTopic = async (userId, topic) => { ); logger.info( - `${userId}'s ${successCount} token(s) unsubscribed from ${topic}` + `${userId}'s ${failureCount} token(s) were not unsubscribed from ${topic}` ); - return successCount; + return failureCount; } catch (error) { logger.error(error); return -1; From c5e4fdb441266b0b5690af4014bf1184722e9dda Mon Sep 17 00:00:00 2001 From: withsang Date: Thu, 16 Feb 2023 08:35:13 +0900 Subject: [PATCH 116/308] Refactor: remove unnecessary imports --- src/service/chats.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/chats.js b/src/service/chats.js index b8022133..d349cac7 100644 --- a/src/service/chats.js +++ b/src/service/chats.js @@ -1,4 +1,4 @@ -const { chatModel, userModel, roomModel } = require("../db/mongo"); +const { chatModel, userModel } = require("../db/mongo"); const awsS3 = require("../db/awsS3"); const { emitChatEvent } = require("../route/chats.socket"); From 65aa9f1a5aebbd76b6a33eacc8280365cb5c8db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeong=20Sang=20=28=EC=A0=95=EC=83=81=29?= Date: Fri, 17 Feb 2023 13:57:20 +0900 Subject: [PATCH 117/308] Add: add default s3Url as suggested by @14KGun Co-authored-by: Geon Kim --- security.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security.js b/security.js index 147b8e8b..3efe3fc2 100644 --- a/security.js +++ b/security.js @@ -16,7 +16,7 @@ module.exports = { accessKeyId: process.env.AWS_ACCESS_KEY_ID, // required secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, // required s3BucketName: process.env.AWS_S3_BUCKET_NAME, // required - s3Url: process.env.AWS_S3_URL || "", // optional + s3Url: process.env.AWS_S3_URL || "https://${process.env.AWS_S3_BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com", // optional }, jwtSecretKey: process.env.JWT_SECRET_KEY, appUriScheme: process.env.APP_URI_SCHEME, From e8ff186dba2a64087cc89e60a1d78750da16caef Mon Sep 17 00:00:00 2001 From: withsang Date: Fri, 17 Feb 2023 15:42:51 +0900 Subject: [PATCH 118/308] Refactor: remove duplicate device token registration codes --- .env.example | 2 +- src/service/auth.mobile.js | 23 ++++++----------------- src/service/auth.replace.js | 23 ++--------------------- 3 files changed, 9 insertions(+), 39 deletions(-) diff --git a/.env.example b/.env.example index 21529016..7c5dd279 100644 --- a/.env.example +++ b/.env.example @@ -11,4 +11,4 @@ AWS_S3_BUCKET_NAME=[AWS S3 Buck name] AWS_S3_URL=[AWS S3 url(e.g. https://.s3..amazonaws.com)] JWT_SECRET_KEY=[JWT SERCRET KEY] APP_URI_SCHEME=[APP_URI_SCHEME] -GOOGLE_APPLICATION_CREDENTIALS=firebase-admin-sdk-account.json \ No newline at end of file +GOOGLE_APPLICATION_CREDENTIALS=firebase-admin-sdk-account.json diff --git a/src/service/auth.mobile.js b/src/service/auth.mobile.js index ae4ad656..7ff8b279 100644 --- a/src/service/auth.mobile.js +++ b/src/service/auth.mobile.js @@ -2,6 +2,10 @@ const { userModel } = require("../db/mongo"); const { deviceTokenModel } = require("../db/mongo"); const { login } = require("../auth/login"); +const { + registerDeviceToken, + unregisterDeviceToken, +} = require("../modules/fcm"); const jwt = require("../modules/jwt"); const logger = require("../modules/logger"); @@ -93,16 +97,7 @@ const registerDeviceTokenHandler = async (req, res) => { ) return res.status(401).send("unauthorized"); - await deviceTokenModel.updateOne( - { - userId: accessTokenStatus.id, - }, - { - userId: accessTokenStatus.id, - $addToSet: { deviceTokens: deviceToken }, - }, - { upsert: true, new: true } - ); + await registerDeviceToken(accessTokenStatus.id, deviceToken); res.status(200).send("success"); } catch (e) { logger.error(e); @@ -124,13 +119,7 @@ const removeDeviceTokenHandler = async (req, res) => { ) return res.status(401).send("unauthorized"); - await deviceTokenModel.updateOne( - { - userId: accessTokenStatus.id, - }, - { userId: accessTokenStatus.id, $pull: { deviceTokens: deviceToken } }, - { upsert: true, new: true } - ); + await unregisterDeviceToken(accessTokenStatus.id, deviceToken); res.status(200).send("success"); } catch (e) { logger.error(e); diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index ea16ee6d..3eefeb0c 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -12,6 +12,8 @@ const { } = require("../modules/modifyProfile"); const logger = require("../modules/logger"); +const { registerDeviceTokenHandler } = require("../service/auth"); + const loginHtml = ` @@ -147,27 +149,6 @@ const logoutHandler = async (req, res) => { } }; -const registerDeviceTokenHandler = async (req, res) => { - try { - const deviceToken = req.body.deviceToken; - // 데이터베이스에 새 레코드를 추가합니다. - const user = await userModel.findOne({ id: req.userId }, "_id"); - - // DB에 deviceToken 레코드를 추가합니다. - const newDeviceToken = await registerDeviceToken(user._id, deviceToken); - - // 세션에 현재 사용자 기기의 deviceToken을 저장합니다. - req.session.deviceToken = deviceToken; - - return res.status(200).json({ - deviceToken: newDeviceToken, - }); - } catch (e) { - logger.error(e); - res.status(500).send("internal server error"); - } -}; - module.exports = { tryHandler, sparcsssoHandler, From 734f024fbbb3144b238d30dfb25558239a09eb52 Mon Sep 17 00:00:00 2001 From: withsang Date: Fri, 17 Feb 2023 19:24:40 +0900 Subject: [PATCH 119/308] Add: validate device token before register --- src/modules/fcm.js | 31 +++++++++++++++++++++++++++---- src/service/auth.js | 15 +++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 0f80dd62..e6b90c80 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -52,6 +52,28 @@ const unregisterDeviceToken = async (userId, deviceToken) => { } }; +/** + * 사용자의 FCM device token이 현재 사용 가능한지 검증합니다. + * @summary 해당 디바이스에 dry-run 방식으로 메시지 전송을 시험함으로써 해당 deviceToken이 사용 가능한지 검증합니다. dry-run 시 FCM 서버에는 메시지 전송 요청이 전송되지만, 실제 기기에는 알림이 전송되지 않습니다. + * @param {string} deviceToken - 사용 가능 여부를 확인하려고 하는 FCM device token입니다. + * @return {Promise} 해당 디바이스에 알림을 보낸다는 요청을 FCM 서버에 성공적으로 보냈으면 true, 아니면 false를 반환합니다. + */ +const validateDeviceToken = async (deviceToken) => { + try { + const message = { + token: deviceToken, + data: { + dryRun: "true", + }, + }; + await getMessaging().send(message, true); + return true; + } catch (error) { + logger.error(error); + return false; + } +}; + /** * 사용자들의 ObjectId의 배열이 주어졌을 때, 해당 사용자들의 모든 deviceToken을 하나의 Array로 반환합니다. * @param {Array} userIds - 사용자의 ObjectId로 이루어진 Array입니다. @@ -78,7 +100,7 @@ const getTokensOfUsers = async (userIds) => { * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. * @param {string} body - 보낼 메시지의 본문입니다. - * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @param {string?} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. * @param {string?} link - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 메시지 알림 전송에 실패한 기기의 수를 반환합니다. 오류가 발생하면 -1을 반환합니다. */ @@ -93,7 +115,7 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { }, webpush: { notification: { - icon, + icon: icon || "/icons-512.png", }, fcm_options: { link: link || "/", @@ -115,7 +137,7 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. * @param {string} body - 보낼 메시지의 본문입니다. - * @param {string} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. + * @param {string?} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. * @param {string?} link - 메시지 알림 팝업을 클릭했을 때 이동할 주소입니다. * @return {Promise} 메시지 알림 전송에 성공했으면 true, 아니면 false를 반환합니다. */ @@ -129,7 +151,7 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { }, webpush: { notification: { - icon, + icon: icon || "/icons-512.png", }, fcm_options: { link: link || "/", @@ -240,6 +262,7 @@ const unsubscribeUserFromTopic = async (userId, topic) => { module.exports = { registerDeviceToken, unregisterDeviceToken, + validateDeviceToken, getTokensOfUsers, sendMessageByTokens, sendMessageByTopic, diff --git a/src/service/auth.js b/src/service/auth.js index 53d9ab89..329ec082 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -6,6 +6,7 @@ const { getLoginInfo, logout, login } = require("../auth/login"); const { registerDeviceToken, unregisterDeviceToken, + validateDeviceToken, } = require("../modules/fcm"); const logger = require("../modules/logger"); const { @@ -182,11 +183,17 @@ const logoutHandler = async (req, res) => { const registerDeviceTokenHandler = async (req, res) => { try { + // 해당 FCM device token이 유효한지 검사합니다. const deviceToken = req.body.deviceToken; - // 데이터베이스에 새 레코드를 추가합니다. - const user = await userModel.findOne({ id: req.userId }, "_id"); + const isValid = await validateDeviceToken(deviceToken); + if (!isValid) { + return res + .status(400) + .send("Auth/registerDeviceToken : deviceToken is invalid"); + } - // DB에 deviceToken 레코드를 추가합니다. + // 데이터베이스에 deviceToken 레코드를 추가합니다. + const user = await userModel.findOne({ id: req.userId }, "_id"); const newDeviceToken = await registerDeviceToken(user._id, deviceToken); // 세션에 현재 사용자 기기의 deviceToken을 저장합니다. @@ -197,7 +204,7 @@ const registerDeviceTokenHandler = async (req, res) => { }); } catch (e) { logger.error(e); - res.status(500).send("internal server error"); + res.status(500).send("Auth/registerDeviceToken : internal server error"); } }; From 93ebeb78bdd2ac5e183751a4147de88db4270ae9 Mon Sep 17 00:00:00 2001 From: ptptsw Date: Tue, 28 Feb 2023 23:12:27 +0900 Subject: [PATCH 120/308] Refactor: renmove payment,settlement socket type --- src/route/chats.socket.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 10c2e61a..9ada5a94 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -39,12 +39,7 @@ const emitChatEvent = async (io, roomId, chat) => { chat.authorName = author.nickname; chat.authorProfileUrl = author.profileImageUrl; - if ( - chat.type == "in" || - chat.type == "out" || - chat.type == "payment" || - chat.type == "settlement" - ) { + if (chat.type == "in" || chat.type == "out") { const userIds = chat.content.split("|"); chat.inOutNames = []; for (const userId of userIds) { @@ -76,8 +71,6 @@ const transformChatsForRoom = async (chats) => { if ( chat.type === "in" || chat.type === "out" || - chat.type === "payment" || - chat.type === "settlement" ) { const inOutUserIds = chat.content.split("|"); chat.inOutNames = await Promise.all( From 051b7670cc0c58cf22cc82b31aa4eb7dc74640bf Mon Sep 17 00:00:00 2001 From: ptptsw Date: Tue, 28 Feb 2023 23:14:29 +0900 Subject: [PATCH 121/308] Refactor: typo --- src/route/chats.socket.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 9ada5a94..67337745 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -68,10 +68,7 @@ const transformChatsForRoom = async (chats) => { for (const chat of chats) { // inOutNames 배열(들어오거나 나간 사용자들의 닉네임으로 이루어진 배열)을 생성합니다. chat.inOutNames = []; - if ( - chat.type === "in" || - chat.type === "out" || - ) { + if (chat.type === "in" || chat.type === "out") { const inOutUserIds = chat.content.split("|"); chat.inOutNames = await Promise.all( inOutUserIds.map(async (userId) => { From 8a6a75dfd0cbe37ee16b284b73d556a093fdb64f Mon Sep 17 00:00:00 2001 From: Hyogyeong8 Date: Tue, 7 Mar 2023 20:57:24 +0900 Subject: [PATCH 122/308] Fix: admin.logs.js PWD to cwd --- src/route/admin.logs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/route/admin.logs.js b/src/route/admin.logs.js index d3ca8bb1..701e0040 100644 --- a/src/route/admin.logs.js +++ b/src/route/admin.logs.js @@ -7,6 +7,6 @@ const router = express.Router(); router.use(require("../middleware/adminAuth")); // Log 파일 제공 -router.use(express.static(path.join(process.env.PWD, "logs"))); +router.use(express.static(path.join(process.cwd(), "logs"))); module.exports = router; From 6e1153f8c313d44a25f3628a67eba741b491f46d Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 7 Mar 2023 21:06:49 +0900 Subject: [PATCH 123/308] Add: add notification option schame --- app.js | 18 +++++++++++------- src/db/mongo.js | 35 ++++++++++++++++++++++++++++++++--- src/modules/fcm.js | 22 ++++++++++++++++++---- src/route/chats.socket.js | 4 +++- 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/app.js b/app.js index 92d9e5e4..5e6e5697 100644 --- a/app.js +++ b/app.js @@ -7,14 +7,18 @@ const logAPIAccess = require("./src/modules/logAPIAccess"); const startSocketServer = require("./src/modules/socket"); // Firebase Admin 초기설정 - const admin = require("firebase-admin"); - -const serviceAccount = require(security.googleApplicationCredentials); - -admin.initializeApp({ - credential: admin.credential.cert(serviceAccount), -}); +const googleApplicationCredentialsPath = security.googleApplicationCredentials; +if (googleApplicationCredentialsPath) { + const serviceAccount = require(googleApplicationCredentialsPath); + admin.initializeApp({ + credential: admin.credential.cert(serviceAccount), + }); +} else { + logger.error( + "Firebase 관련 credential이 존재하지 않습니다. FCM 관련 기능을 사용할 수 없습니다." + ); +} // 익스프레스 서버 생성 const app = express(); diff --git a/src/db/mongo.js b/src/db/mongo.js index 46cdb51b..9908b8df 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -36,6 +36,36 @@ const participantSchema = Schema({ }, }); +// 각 사용자의 알림 설정 +const notificationOptionSchema = Schema({ + chatting: { + type: Boolean, + default: true, + required: true, + }, //채팅 알림 수신 여부 + keywords: [ + { + type: String, + required: true, + }, + ], //방 알림 키워드 + beforeDepart: { + type: Boolean, + required: true, + default: false, + }, //출발 전 알림 발송 유무 + notice: { + type: Boolean, + default: true, + required: true, + }, //공지 알림 수신 여부 + advertisement: { + type: Boolean, + default: false, + required: true, + }, //광고성 알림 수신 여부 +}); + const deviceTokenSchema = Schema({ userId: { type: Schema.Types.ObjectId, @@ -44,9 +74,8 @@ const deviceTokenSchema = Schema({ unique: true, }, deviceTokens: [{ type: String, required: true }], - registeredAt: { - type: Date, - default: () => Date.now(), + notificationOptions: { + type: notificationOptionSchema, required: true, }, }); diff --git a/src/modules/fcm.js b/src/modules/fcm.js index e6b90c80..233b58e2 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -27,6 +27,7 @@ const registerDeviceToken = async (userId, deviceToken) => { } }; +// TODO: remove userId /** * 사용자의 ObjectId와 FCM device token이 주어졌을 때, 해당 사용자의 해당 deviceToken을 DB에서 삭제합니다. * @param {string} userId - 사용자의 ObjectId입니다. @@ -77,17 +78,29 @@ const validateDeviceToken = async (deviceToken) => { /** * 사용자들의 ObjectId의 배열이 주어졌을 때, 해당 사용자들의 모든 deviceToken을 하나의 Array로 반환합니다. * @param {Array} userIds - 사용자의 ObjectId로 이루어진 Array입니다. + * @param {Object?} notificationOptions - 특정 알림 설정을 비활성화한 사용자를 필터링하기 위해 사용되는 Object입니다. + * @param {Boolean?} notificationOptions.chatting - true 또는 false로 주어진 경우, 채팅 알림 설정이 각각 true 또는 false로 설정된 사용자들의 deviceToken만 반환합니다. * @return {Promise>} deviceToken의 Array를 반환합니다. 오류가 발생하면 빈 배열을 반환합니다. */ -const getTokensOfUsers = async (userIds) => { +const getTokensOfUsers = async (userIds, notificationOptions = {}) => { const deviceTokensOfUsers = await Promise.all( userIds.map(async (userId) => { - const deviceToken = await deviceTokenModel.findOne({ - userId, - }); + const query = { userId }; + if (notificationOptions) query.notificationOptions = notificationOptions; + const deviceToken = await deviceTokenModel.findOne({ query }); return deviceToken?.deviceTokens || new Array(); }) ); + logger.info( + await Promise.all( + userIds.map(async (userId) => { + const deviceToken = await deviceTokenModel.findOne({ + userId, + }); + return deviceToken?.deviceTokens || new Array(); + }) + ) + ); return deviceTokensOfUsers.reduce( (arrayA, arrayB) => arrayA.concat(arrayB), new Array() @@ -96,6 +109,7 @@ const getTokensOfUsers = async (userIds) => { /** * 주어진 token들에 메시지 알림을 전송합니다. + * TODO: 알림 전송 실패한 토큰 삭제하기 * @param {Array} tokens - 메시지 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index ce939ce4..5aba359b 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -104,7 +104,9 @@ const emitChatEvent = async (io, roomId, chat) => { const userIdsExceptAuthor = room.part .map((participant) => participant.user) .filter((userId) => userId.toString() !== authorId.toString()); - const deviceTokens = await getTokensOfUsers(userIdsExceptAuthor); + const deviceTokens = await getTokensOfUsers(userIdsExceptAuthor, { + chatting: true, + }); // 해당 방에 참여중인 사용자들에게 알림을 전송합니다. await sendMessageByTokens( From 9e6d805949d65d02876d1f4985aec1fc470a4fe2 Mon Sep 17 00:00:00 2001 From: Dongwon Date: Tue, 7 Mar 2023 22:21:20 +0900 Subject: [PATCH 124/308] Fix: add account in findOne call --- src/service/logininfo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/logininfo.js b/src/service/logininfo.js index 056b6072..211e7f2e 100644 --- a/src/service/logininfo.js +++ b/src/service/logininfo.js @@ -11,7 +11,7 @@ const detailHandler = (req, res) => { if (!user.id) return res.json({ id: undefined }); userModel.findOne( { id: user.id }, - "_id name nickname id withdraw ban joinat agreeOnTermsOfService subinfo email profileImageUrl", + "_id name nickname id withdraw ban joinat agreeOnTermsOfService subinfo email profileImageUrl account", (err, result) => { if (err) res.json({ err: true }); else if (!result) res.json({ err: true }); From 87053513e5bffb35d616caed0d26ee4ab74e706d Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 8 Mar 2023 09:13:32 +0000 Subject: [PATCH 125/308] Add: chat type in chatSchema --- src/db/mongo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/mongo.js b/src/db/mongo.js index 9bd1dbcf..4ebb9f16 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -76,7 +76,7 @@ const chatSchema = Schema({ roomId: { type: Schema.Types.ObjectId, ref: "Room", required: true }, type: { type: String, - enum: ["text", "in", "out", "s3img", "payment", "settlement"], + enum: ["text", "in", "out", "s3img", "payment", "settlement", "account"], }, // 메시지 종류 authorId: { type: Schema.Types.ObjectId, ref: "User", required: true }, // 작성자 id content: { type: String, default: "" }, From 4808e09c8b39ee13318dc7b5437454a9066c8a23 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 8 Mar 2023 09:36:14 +0000 Subject: [PATCH 126/308] Refactor: change emitChatEvent in chats-send --- src/route/chats.socket.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 67337745..eb23f943 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -172,7 +172,7 @@ const ioListeners = (io, socket) => { .emit("chats-send", { err: "user not join chat room" }); emitChatEvent(io, roomId, { - type: "text", + type: chatMessage.type || "text", content: chatMessage.content, authorId: myUser._id, }); From ff8529424f8936ba14e0d5d68d7a949fe355dd43 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Mon, 13 Mar 2023 16:14:10 +0900 Subject: [PATCH 127/308] Rename: authAdmin --- src/middleware/{adminAuth.js => authAdmin.js} | 4 ++-- src/route/admin.js | 2 +- src/route/admin.logs.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/middleware/{adminAuth.js => authAdmin.js} (91%) diff --git a/src/middleware/adminAuth.js b/src/middleware/authAdmin.js similarity index 91% rename from src/middleware/adminAuth.js rename to src/middleware/authAdmin.js index d6bb6d07..b426135b 100644 --- a/src/middleware/adminAuth.js +++ b/src/middleware/authAdmin.js @@ -4,7 +4,7 @@ const { isLogin, getLoginInfo } = require("../auth/login"); const { frontUrl } = require("../../security"); const { userModel, adminIPWhitelistModel } = require("../db/mongo"); -const adminAuthMiddleware = async (req, res, next) => { +const authAdminMiddleware = async (req, res, next) => { try { // 로그인 여부를 확인 if (!isLogin(req)) return res.redirect(frontUrl); @@ -31,4 +31,4 @@ const adminAuthMiddleware = async (req, res, next) => { } }; -module.exports = adminAuthMiddleware; +module.exports = authAdminMiddleware; diff --git a/src/route/admin.js b/src/route/admin.js index 816d5f4b..8085ca3f 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -14,7 +14,7 @@ const { let router = express.Router(); // Requires admin property of the user to enter admin page. -router.use(require("../middleware/adminAuth")); +router.use(require("../middleware/authAdmin")); // Registration of the mongoose adapter AdminJS.registerAdapter(AdminJSMongoose); diff --git a/src/route/admin.logs.js b/src/route/admin.logs.js index 701e0040..619ab3f8 100644 --- a/src/route/admin.logs.js +++ b/src/route/admin.logs.js @@ -4,7 +4,7 @@ const path = require("path"); const router = express.Router(); // Requires admin property of the user to enter admin page. -router.use(require("../middleware/adminAuth")); +router.use(require("../middleware/authAdmin")); // Log 파일 제공 router.use(express.static(path.join(process.cwd(), "logs"))); From 967f02805474525f3a79c3b46e32ade9d41d6821 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Mon, 13 Mar 2023 16:24:25 +0900 Subject: [PATCH 128/308] Refactor: timestamp middleware --- app.js | 3 +++ src/middleware/{setTimestamp.js => timestamp.js} | 0 src/route/auth.js | 5 +---- src/route/auth.replace.js | 3 +-- src/route/rooms.js | 4 ---- 5 files changed, 5 insertions(+), 10 deletions(-) rename src/middleware/{setTimestamp.js => timestamp.js} (100%) diff --git a/app.js b/app.js index ca3650a7..634a4fb9 100644 --- a/app.js +++ b/app.js @@ -24,6 +24,9 @@ app.use(require("cookie-parser")()); // [Middleware] API 접근 기록 및 응답 시간을 http response의 헤더에 기록합니다. app.use(require("response-time")(logAPIAccess)); +// [Middleware] Timestamp 확인 +app.use(require("./src/middleware/timestamp")); + // [Router] admin 페이지는 rate limiting을 적용하지 않습니다. app.use("/admin/logs", require("./src/route/admin.logs")); app.use("/admin", require("./src/route/admin")); diff --git a/src/middleware/setTimestamp.js b/src/middleware/timestamp.js similarity index 100% rename from src/middleware/setTimestamp.js rename to src/middleware/timestamp.js diff --git a/src/route/auth.js b/src/route/auth.js index fc83f8b3..1fb74f98 100644 --- a/src/route/auth.js +++ b/src/route/auth.js @@ -3,14 +3,11 @@ const security = require("../../security"); const authReplace = require("./auth.replace"); const router = express.Router(); -const setTimestamp = require("../middleware/setTimestamp"); const authHandlers = require("../service/auth"); const mobileAuthHandlers = require("../service/auth.mobile"); router.route("/sparcssso").get(authHandlers.sparcsssoHandler); -router - .route("/sparcssso/callback") - .get(setTimestamp, authHandlers.sparcsssoCallbackHandler); +router.route("/sparcssso/callback").get(authHandlers.sparcsssoCallbackHandler); router.route("/logout").get(authHandlers.logoutHandler); router.route("/app/token/login").get(mobileAuthHandlers.loginWithToken); diff --git a/src/route/auth.replace.js b/src/route/auth.replace.js index 0eb02edf..e274fa40 100755 --- a/src/route/auth.replace.js +++ b/src/route/auth.replace.js @@ -2,10 +2,9 @@ const express = require("express"); const router = express.Router(); const authReplaceHandlers = require("../service/auth.replace"); -const setTimestamp = require("../middleware/setTimestamp"); // 로그인 시도 -router.route("/try").post(setTimestamp, authReplaceHandlers.tryHandler); +router.route("/try").post(authReplaceHandlers.tryHandler); // html 로그인 페이지 쏴주기 router.route("/sparcssso").get(authReplaceHandlers.sparcsssoHandler); diff --git a/src/route/rooms.js b/src/route/rooms.js index d43e4352..c463efe8 100644 --- a/src/route/rooms.js +++ b/src/route/rooms.js @@ -5,7 +5,6 @@ const router = express.Router(); const roomHandlers = require("../service/rooms"); const validator = require("../middleware/validator"); const patterns = require("../db/patterns"); -const setTimestamp = require("../middleware/setTimestamp"); // 라우터 접근 시 로그인 필요 router.use(require("../middleware/auth")); @@ -38,7 +37,6 @@ router.post( "/join", [body("roomId").isMongoId()], validator, - setTimestamp, roomHandlers.joinHandler ); @@ -50,7 +48,6 @@ router.post( "/abort", body("roomId").isMongoId(), validator, - setTimestamp, roomHandlers.abortHandler ); @@ -77,7 +74,6 @@ router.post( "/commitPayment", body("roomId").isMongoId(), validator, - setTimestamp, roomHandlers.commitPaymentHandler ); From 007572bf8b7fefea36d39db7c4bed0359aa79e1d Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Mon, 13 Mar 2023 19:13:09 +0900 Subject: [PATCH 129/308] Update: adminJS --- package-lock.json | 3501 ++++++++++++++++++++++++-------------------- package.json | 6 +- src/route/admin.js | 9 +- 3 files changed, 1921 insertions(+), 1595 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7988aef8..8e9d2e42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@adminjs/express": "^5.0.1", - "@adminjs/mongoose": "^3.0.1", - "adminjs": "^6.5.0", + "@adminjs/express": "^5.1.0", + "@adminjs/mongoose": "^3.0.3", + "adminjs": "^6.8.7", "aws-sdk": "^2.1182.0", "axios": "^0.27.2", "connect-mongo": "^4.6.0", @@ -49,40 +49,32 @@ "supertest": "^6.2.4" } }, - "node_modules/@_ueberdosis/prosemirror-tables": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@_ueberdosis/prosemirror-tables/-/prosemirror-tables-1.1.3.tgz", - "integrity": "sha512-su3pbFi1DT89g6Cuh72TE0MWWKHmWgHcQJ3ODRkm6XfIppWaGpU49t02ur3sgJc7hUhfQXjB93aSkDgOmIii2w==", - "dependencies": { - "prosemirror-keymap": "^1.1.2", - "prosemirror-model": "^1.8.1", - "prosemirror-state": "^1.3.1", - "prosemirror-transform": "^1.2.1", - "prosemirror-view": "^1.13.3" - } - }, "node_modules/@adminjs/design-system": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@adminjs/design-system/-/design-system-3.1.0.tgz", - "integrity": "sha512-DRvHWzorYqOwtHr1MSp3WVEs5aLLv9P9msWw0qBdZaRwwb18mAT3z93/FjaclXQ4fBGENRlAdjezS1hewLLjLw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@adminjs/design-system/-/design-system-3.1.8.tgz", + "integrity": "sha512-M0l8NXoHKFoJ9XLv6BkrgRPnE0hCYNYWVNiQKA4qOpzifB2LAPAViqQ36Qyxgz1mL9nnzl7OJpGlb8cHSrIajg==", "dependencies": { "@carbon/icons-react": "^11.8.0", "@hypnosphi/create-react-context": "^0.3.1", - "@tiptap/extension-character-count": "2.0.0-beta.31", - "@tiptap/extension-code": "2.0.0-beta.28", - "@tiptap/extension-document": "2.0.0-beta.17", - "@tiptap/extension-heading": "2.0.0-beta.29", - "@tiptap/extension-image": "2.0.0-beta.30", - "@tiptap/extension-link": "2.0.0-beta.43", - "@tiptap/extension-table": "2.0.0-beta.54", - "@tiptap/extension-table-cell": "2.0.0-beta.23", - "@tiptap/extension-table-header": "2.0.0-beta.25", - "@tiptap/extension-table-row": "2.0.0-beta.22", - "@tiptap/extension-text": "2.0.0-beta.17", - "@tiptap/extension-text-align": "2.0.0-beta.31", - "@tiptap/extension-typography": "2.0.0-beta.22", - "@tiptap/react": "2.0.0-beta.114", - "@tiptap/starter-kit": "2.0.0-beta.191", + "@tiptap/core": "2.0.0-beta.217", + "@tiptap/extension-bubble-menu": "2.0.0-beta.217", + "@tiptap/extension-character-count": "2.0.0-beta.217", + "@tiptap/extension-code": "2.0.0-beta.217", + "@tiptap/extension-document": "2.0.0-beta.217", + "@tiptap/extension-floating-menu": "2.0.0-beta.217", + "@tiptap/extension-heading": "2.0.0-beta.217", + "@tiptap/extension-image": "2.0.0-beta.217", + "@tiptap/extension-link": "2.0.0-beta.217", + "@tiptap/extension-table": "2.0.0-beta.217", + "@tiptap/extension-table-cell": "2.0.0-beta.217", + "@tiptap/extension-table-header": "2.0.0-beta.217", + "@tiptap/extension-table-row": "2.0.0-beta.217", + "@tiptap/extension-text": "2.0.0-beta.217", + "@tiptap/extension-text-align": "2.0.0-beta.217", + "@tiptap/extension-typography": "2.0.0-beta.217", + "@tiptap/pm": "2.0.0-beta.217", + "@tiptap/react": "2.0.0-beta.217", + "@tiptap/starter-kit": "2.0.0-beta.217", "date-fns": "2.15.0", "jw-paginate": "^1.0.4", "lodash": "^4.17.20", @@ -102,9 +94,10 @@ } }, "node_modules/@adminjs/express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@adminjs/express/-/express-5.0.1.tgz", - "integrity": "sha512-C/5sCizoUN7OvVVANj7hegHJM8vHHfzbcKkCMdbXj8kqdISP4gyTgGFsBXADQTHscLMtn3/MdXv9XfS6iaBU0A==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@adminjs/express/-/express-5.1.0.tgz", + "integrity": "sha512-+mrtDmoAYA9R+/FTYWOLL48g005yrgcAWC2phdwqGzznIxGKSp2YERcfzdTI7Svtnlaal72/QW8Q3OhzJjVLzQ==", + "license": "SEE LICENSE IN LICENSE", "dependencies": { "path-to-regexp": "^6.2.0" }, @@ -117,12 +110,12 @@ } }, "node_modules/@adminjs/mongoose": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@adminjs/mongoose/-/mongoose-3.0.1.tgz", - "integrity": "sha512-HX30VuHhE3KH2xL/E9XPe6ohExjfy9GecIMmP7OygUt1dRBJY2u/hRzZ09bf8HsOCRrR5yA8AbuxPBi1hGiQ1A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@adminjs/mongoose/-/mongoose-3.0.3.tgz", + "integrity": "sha512-J/Ogz3oJ2ytOsbeqBpjgIFtiAmGk3MVVfJq2cUidXJ1phrvNHhb7AjiaKd+pcdFcT84COUHaoo6uPYvrLhZEQg==", + "license": "MIT", "dependencies": { "escape-regexp": "0.0.1", - "flat": "^4.1.0", "lodash": "^4.17.21" }, "peerDependencies": { @@ -194,32 +187,32 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", + "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", "dependencies": { - "@ampproject/remapping": "^2.1.0", + "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", + "@babel/generator": "^7.21.0", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.0", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.0", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" }, "engines": { @@ -231,12 +224,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", "dependencies": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.21.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -280,13 +274,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "dependencies": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.20.5", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", "semver": "^6.3.0" }, "engines": { @@ -296,17 +291,31 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", - "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", + "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/helper-split-export-declaration": "^7.18.6" }, "engines": { @@ -317,12 +326,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", - "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", + "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.1.0" + "regexpu-core": "^5.3.1" }, "engines": { "node": ">=6.9.0" @@ -332,9 +341,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", - "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dependencies": { "@babel/helper-compilation-targets": "^7.17.7", "@babel/helper-plugin-utils": "^7.16.7", @@ -367,12 +376,12 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -390,11 +399,11 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", - "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", + "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", "dependencies": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -412,18 +421,18 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" }, "engines": { "node": ">=6.9.0" @@ -441,9 +450,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "engines": { "node": ">=6.9.0" } @@ -466,37 +475,38 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", - "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.20.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dependencies": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -514,51 +524,51 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz", - "integrity": "sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", "dependencies": { - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.11", - "@babel/types": "^7.18.10" + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -578,9 +588,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -603,13 +613,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", - "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-proposal-optional-chaining": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -619,12 +629,12 @@ } }, "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", - "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" }, @@ -651,12 +661,12 @@ } }, "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", - "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -712,11 +722,11 @@ } }, "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", - "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -757,15 +767,15 @@ } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", - "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" + "@babel/plugin-transform-parameters": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -790,12 +800,12 @@ } }, "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", - "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -821,13 +831,13 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", - "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", + "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -911,11 +921,11 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", - "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1044,11 +1054,11 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1058,11 +1068,11 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", - "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", + "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1072,13 +1082,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", - "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", "dependencies": { "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-remap-async-to-generator": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1102,11 +1112,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", - "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", + "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1116,16 +1126,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", - "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", + "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" }, @@ -1137,11 +1148,12 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", - "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", + "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/template": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -1151,11 +1163,11 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", - "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", + "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1209,11 +1221,11 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", - "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", + "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1267,13 +1279,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", - "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1283,14 +1294,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", - "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", + "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-simple-access": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1300,15 +1310,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", - "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", "dependencies": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" }, "engines": { "node": ">=6.9.0" @@ -1333,12 +1342,12 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", - "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1377,11 +1386,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", + "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1419,15 +1428,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz", - "integrity": "sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz", + "integrity": "sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.18.10" + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -1466,12 +1475,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", - "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "regenerator-transform": "^0.15.0" + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" }, "engines": { "node": ">=6.9.0" @@ -1495,15 +1504,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", - "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz", + "integrity": "sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg==", "dependencies": { "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", + "@babel/helper-plugin-utils": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", "semver": "^6.3.0" }, "engines": { @@ -1528,12 +1537,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", - "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -1585,13 +1594,13 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.12.tgz", - "integrity": "sha512-2vjjam0cum0miPkenUbQswKowuxs/NjMwIKEq0zwegRxXk12C9YOF9STXnaUptITOtOJHKHpzvvWYOjbm6tc0w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz", + "integrity": "sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-typescript": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -1640,17 +1649,17 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", - "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-validator-option": "^7.18.6", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-static-block": "^7.18.6", "@babel/plugin-proposal-dynamic-import": "^7.18.6", @@ -1659,7 +1668,7 @@ "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", "@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-private-methods": "^7.18.6", @@ -1670,7 +1679,7 @@ "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -1683,10 +1692,10 @@ "@babel/plugin-transform-arrow-functions": "^7.18.6", "@babel/plugin-transform-async-to-generator": "^7.18.6", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.18.9", - "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", @@ -1694,30 +1703,30 @@ "@babel/plugin-transform-function-name": "^7.18.9", "@babel/plugin-transform-literals": "^7.18.9", "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-parameters": "^7.20.1", "@babel/plugin-transform-property-literals": "^7.18.6", "@babel/plugin-transform-regenerator": "^7.18.6", "@babel/plugin-transform-reserved-words": "^7.18.6", "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-spread": "^7.19.0", "@babel/plugin-transform-sticky-regex": "^7.18.6", "@babel/plugin-transform-template-literals": "^7.18.9", "@babel/plugin-transform-typeof-symbol": "^7.18.9", "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.18.10", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "core-js-compat": "^3.22.1", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" }, "engines": { @@ -1762,13 +1771,13 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz", + "integrity": "sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-transform-typescript": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -1778,9 +1787,9 @@ } }, "node_modules/@babel/register": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.18.9.tgz", - "integrity": "sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.21.0.tgz", + "integrity": "sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw==", "dependencies": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", @@ -1795,43 +1804,48 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, "node_modules/@babel/runtime": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", - "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", "dependencies": { - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1840,12 +1854,12 @@ } }, "node_modules/@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1853,17 +1867,17 @@ } }, "node_modules/@carbon/icon-helpers": { - "version": "10.34.0", - "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.34.0.tgz", - "integrity": "sha512-Ov9EBc1tR/DDrMI0pN1drj2jb27ISmYFBLdDji+aivVJkLPy8R/jikJOsOBgIq2kUjQJYNN199k2acHKjZdYIg==" + "version": "10.39.0", + "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.39.0.tgz", + "integrity": "sha512-iFWIfjKABjusb+gUz6s0FdEBHe8Ms63CKDxozhtiSZ9LfF9X5QQztO8df3szqcNsmw30pYhSnm+zJwifO9tdRw==" }, "node_modules/@carbon/icons-react": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.10.0.tgz", - "integrity": "sha512-807RWTfbvVzmsDg2DJ4FjwYNbJSgkrEd1Ui8I07YheJVb3sbYGGZMG7aCS0qXVlrQOhB2hggtxSW1w9NksUXNA==", + "version": "11.17.0", + "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.17.0.tgz", + "integrity": "sha512-PheX1aGh12mfybHeLB9w8Y8UzARuqpVanJ3zTDCUQKfPFaOezAt+6E9lqjj5xq3QgpyZcrCEOFiaPV90n3MqGQ==", "hasInstallScript": true, "dependencies": { - "@carbon/icon-helpers": "^10.34.0", + "@carbon/icon-helpers": "^10.39.0", "@carbon/telemetry": "0.1.0", "prop-types": "^15.7.2" }, @@ -1898,12 +1912,11 @@ } }, "node_modules/@emotion/babel-plugin": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", - "integrity": "sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==", + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz", + "integrity": "sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==", "dependencies": { "@babel/helper-module-imports": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.17.12", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.0", "@emotion/memoize": "^0.8.0", @@ -1914,9 +1927,6 @@ "find-root": "^1.1.0", "source-map": "^0.5.7", "stylis": "4.1.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { @@ -1930,14 +1940,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@emotion/babel-plugin/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@emotion/cache": { "version": "11.10.5", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz", @@ -1969,12 +1971,12 @@ "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" }, "node_modules/@emotion/react": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz", - "integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==", + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz", + "integrity": "sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", + "@emotion/babel-plugin": "^11.10.6", "@emotion/cache": "^11.10.5", "@emotion/serialize": "^1.1.1", "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", @@ -1983,13 +1985,9 @@ "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { - "@babel/core": "^7.0.0", "react": ">=16.8.0" }, "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, "@types/react": { "optional": true } @@ -2074,22 +2072,22 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.1.tgz", - "integrity": "sha512-bO37brCPfteXQfFY0DyNDGB3+IMe4j150KFQcgJ5aBP295p9nBGeHEs/p0czrRbtlHq4Px/yoPXO/+dOCcF4uA==" + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.3.tgz", + "integrity": "sha512-upVRtrNZuYNsw+EoxkiBFRPROnU8UTy/u/dZ9U0W14BlemPYODwhhxYXSR2Y9xOnvr1XtptJRWx7gL8Te1qaog==" }, "node_modules/@floating-ui/dom": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.4.tgz", - "integrity": "sha512-maYJRv+sAXTy4K9mzdv0JPyNW5YPVHrqtY90tEdI6XNpuLOP26Ci2pfwPsKBA/Wh4Z3FX5sUrtUFTdMYj9v+ug==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.4.tgz", + "integrity": "sha512-4+k+BLhtWj+peCU60gp0+rHeR8+Ohqx6kjJf/lHMnJ8JD5Qj6jytcq1+SZzRwD7rvHKRhR7TDiWWddrNrfwQLg==", "dependencies": { - "@floating-ui/core": "^1.0.1" + "@floating-ui/core": "^1.2.3" } }, "node_modules/@hello-pangea/dnd": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.0.1.tgz", - "integrity": "sha512-mRWQxRHelK1WAoEYpMfCTOVBIVdmnGXNYmpQUw1LKN/QY6YCNOsUDC4pg3QOtNYRmrxfjCm4HkSgVq5LuD/5ag==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.2.0.tgz", + "integrity": "sha512-inACvMcvvLr34CG0P6+G/3bprVKhwswxjcsFUSJ+fpOGjhvDj9caiA9X3clby0lgJ6/ILIJjyedHZYECB7GAgA==", "dependencies": { "@babel/runtime": "^7.19.4", "css-box-model": "^1.2.1", @@ -2200,12 +2198,12 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "node_modules/@jsdevtools/ono": { @@ -2213,6 +2211,11 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, + "node_modules/@linaria/core": { + "version": "3.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@linaria/core/-/core-3.0.0-beta.13.tgz", + "integrity": "sha512-3zEi5plBCOsEzUneRVuQb+2SAx3qaC1dj0FfFAI6zIJQoDWu0dlSwKijMRack7oO9tUWrchfj3OkKQAd1LBdVg==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2307,10 +2310,59 @@ "@redis/client": "^1.0.0" } }, + "node_modules/@remirror/core-constants": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-2.0.0.tgz", + "integrity": "sha512-vpePPMecHJllBqCWXl6+FIcZqS+tRUM2kSCCKFeEo1H3XUEv3ocijBIPhnlSAa7g6maX+12ATTgxrOsLpWVr2g==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@remirror/core-helpers": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@remirror/core-helpers/-/core-helpers-2.0.1.tgz", + "integrity": "sha512-s8M1pn33aBUhduvD1QR02uUQMegnFkGaTr4c1iBzxTTyg0rbQstzuQ7Q8TkL6n64JtgCdJS9jLz2dONb2meBKQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@linaria/core": "3.0.0-beta.13", + "@remirror/core-constants": "^2.0.0", + "@remirror/types": "^1.0.0", + "@types/object.omit": "^3.0.0", + "@types/object.pick": "^1.3.1", + "@types/throttle-debounce": "^2.1.0", + "case-anything": "^2.1.10", + "dash-get": "^1.0.2", + "deepmerge": "^4.2.2", + "fast-deep-equal": "^3.1.3", + "make-error": "^1.3.6", + "object.omit": "^3.0.0", + "object.pick": "^1.3.0", + "throttle-debounce": "^3.0.1" + } + }, + "node_modules/@remirror/types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@remirror/types/-/types-1.0.0.tgz", + "integrity": "sha512-7HQbW7k8VxrAtfzs9FxwO6XSDabn8tSFDi1wwzShOnU+cvaYpfxu0ygyTk3TpXsag1hgFKY3ZIlAfB4WVz2LkQ==", + "dependencies": { + "type-fest": "^2.0.0" + } + }, + "node_modules/@remirror/types/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@remix-run/router": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", - "integrity": "sha512-ceuyTSs7PZ/tQqi19YZNBc5X7kj1f8p+4DIyrcIYFY9h+hd1OKm4RqtiWldR9eGEvIiJfsqwM4BsuCtRIuEw6Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.4.0.tgz", + "integrity": "sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==", "engines": { "node": ">=14" } @@ -2523,27 +2575,21 @@ } }, "node_modules/@tiptap/core": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.199.tgz", - "integrity": "sha512-34GaXcBEmNFjW1R7nf1LSmOHo3Q81YjKqvLAXjDLLG7MTx+YTrQ4yWwUvMsZtmi4o/FchUzrs1NVCfr571Zxzg==", - "dependencies": { - "prosemirror-commands": "^1.3.1", - "prosemirror-keymap": "^1.2.0", - "prosemirror-model": "^1.18.1", - "prosemirror-schema-list": "^1.2.2", - "prosemirror-state": "^1.4.1", - "prosemirror-transform": "^1.7.0", - "prosemirror-view": "^1.28.2" - }, + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.217.tgz", + "integrity": "sha512-Vifwcg5SglkVjEmtFbnwHOKWU4UUenOhe7ke5fqGhh7FNfGkccu6sK8W1JTDbG4ARWZ1b/632kQ51YE+WuPe7g==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-blockquote": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.199.tgz", - "integrity": "sha512-BbHKaIkVYgJCV5giJC3/bdXMZWxFylLKiAbOGSGwIsnnS5/oL+V4XN6hqcIDBxlcj3MQ/d9zG0+mvFyjRssAkg==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.220.tgz", + "integrity": "sha512-uE1VRU/doQzXsfsZ/JqsbSbXeZYTJnyQkSfHYA2ZYhbEM2XqDEsYkgcmZEJgunUZJpERf+3ZTfTpqaHq29iMMg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -2553,24 +2599,23 @@ } }, "node_modules/@tiptap/extension-bold": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.199.tgz", - "integrity": "sha512-l513jgGLmt8C69Yuh5Et7a46Tn8QpW4q1HhZK6ih0ajNT+L5Xk0CSxEK/K5EmHSACPhwqjsJztLpGjAdoOn0mA==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.220.tgz", + "integrity": "sha512-KcEuKI85Drug/cCWbDy+HxhYrD+rLXHEBG10DmKPvgPpKHG/2wOau6LwUwyV4muWR8CR2mIO+mEc3yVBD8nNwQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-bubble-menu": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.199.tgz", - "integrity": "sha512-T3K8xoDbX6J62lhIUpclQoW/1XFt7yfI5DCoxtVWUeKaF+pG6kdsB3CPG5C/+AQVlz2jSIJmQuPf8RQFpQs+yg==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.217.tgz", + "integrity": "sha512-f52hjIzNbvAJy0P3pTf6sv/65XlMU1LCLXam4VCTkYJ2HHVvW+LZcu+dG1M8YmGkM9l+6dQPs5L2rriAQDXt+w==", "dependencies": { - "prosemirror-state": "^1.4.1", - "prosemirror-view": "^1.28.2", + "lodash": "^4.17.21", "tippy.js": "^6.3.7" }, "funding": { @@ -2578,107 +2623,90 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-bullet-list": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.199.tgz", - "integrity": "sha512-gGRQRqdQqCZQstB3ztSy8yzIdm5/5IIYxhCuFNb3Z9c9p/CzyRmaNqa7XkRLrXSajp4lS0OH8RkFUJqL6U+/9w==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.220.tgz", + "integrity": "sha512-QQ/0ZlYy6Hgb+UAc79V+fxvI+AaQf20cbKtBXaR8TIZ0x4FotSma89bKh+CIXMhFiBGXTcYBaYhl7OwACsKtxw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-character-count": { - "version": "2.0.0-beta.31", - "resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.0.0-beta.31.tgz", - "integrity": "sha512-NNA9MN1IjZe+yYQLuYVAg9RNG/3RonYrHiM5mL6vsegd+PF4uMqyZLgsM0/9dMhxh9K/pDPaCRxhuDoZC8V1wA==", - "dependencies": { - "prosemirror-model": "1.18.1", - "prosemirror-state": "1.4.1" - }, + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.0.0-beta.217.tgz", + "integrity": "sha512-jj3GzuoUbHcPsRyIkxr/X/+FmnvKeDUtzvWhhpEPOlCf9q5Wjq95VbVYZdNPlEXCNCdMu0ePvyW+v8+kFr+Qlw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, - "node_modules/@tiptap/extension-character-count/node_modules/prosemirror-state": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", - "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", - "dependencies": { - "prosemirror-model": "^1.0.0", - "prosemirror-transform": "^1.0.0" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-code": { - "version": "2.0.0-beta.28", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.0.0-beta.28.tgz", - "integrity": "sha512-QPJ2Gwb1+3NgcC1ZIhvVcb+FsnWWDu5VZXTKXM4mz892i9V2x48uHg5anPiUV6pcolXsW1F5VNbXIHGTUUO6CQ==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.0.0-beta.217.tgz", + "integrity": "sha512-IPpuI8MeVX5N0ueWQcIvh2nD+EN6DbFtKSsoU+FgFJ9meOkiEWGibSJ5yFQinPsiE9zrwYIaZzzjhr4oeY2JNA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-code-block": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.199.tgz", - "integrity": "sha512-ZfftYE1kHA2pD46hXDkeYd1vuxp3bJLS854B2yHfw1cp3JVDjMXzm4Mzg7zLfr+YV1dT/N/fUfdCg38fqEUCyA==", - "dependencies": { - "prosemirror-state": "^1.4.1" - }, + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.220.tgz", + "integrity": "sha512-fgA7yTfHqhBtMJF7I9FPJ6UWuZPtxOQiN45Iv9LNmFIB6YRucdpmF+daZ27sElu0a+eICZyXwVn4w4iJphifuw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-document": { - "version": "2.0.0-beta.17", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.0.0-beta.17.tgz", - "integrity": "sha512-L6sg0FNchbtIpQkCSjMmItVGs3/vep8Fq56WRtDc1wBSGUSmtHaxQG7F2FZLnNIUMuvzVMRD81m2vYG73WkY6A==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.0.0-beta.217.tgz", + "integrity": "sha512-CDCTutbVO1Ub7QUULPCYILl2px48ezCX2kxbspQZzPD7nMoYZNmUMZ8gRJvVDdnSpMNlEl7oNnCAU7uC/lny/A==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-dropcursor": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.199.tgz", - "integrity": "sha512-RhdYm0yBJxVLECaHWsZcBIwRJUoUqZ79jvs+kUVodxHW4+IxRAgEA+lImr0GD+kk8aX5Mrk8YhWuUUeu5nzpTg==", - "dependencies": { - "prosemirror-dropcursor": "1.5.0" - }, + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.220.tgz", + "integrity": "sha512-BIaA4Lvb3xL9KFN+K6SO2IHqLO6hDmGN2/rGKHFaU3Eh+oiXM2G73KTSS5KIP1u872zY1RpAtswSc4kjv3cuVw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-floating-menu": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.199.tgz", - "integrity": "sha512-ELjqnNbxW66uqg54zlP2b4EVYUWvT2WvHmeOXALzoLlNzbqUopIl3XNRsvU2Dv1W88C1UjKgnRZIkHKFE1X3CA==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.217.tgz", + "integrity": "sha512-mP77UrimZo3sdC+Xam0DsPeYfXn29hl4ixEuZeiQQBMqz7g1K9T7vOEsRMQCBVFS7Hsc7MWBqLa1lL1GtyBB9Q==", "dependencies": { - "prosemirror-state": "^1.4.1", - "prosemirror-view": "^1.28.2", "tippy.js": "^6.3.7" }, "funding": { @@ -2686,338 +2714,321 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-gapcursor": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.199.tgz", - "integrity": "sha512-0TDpDfDyay+IbD+wJMsBJ2c0Cq0NtllUOxbi0NPjjWW94Jrvs1yqUSzX4Qp9m5MW8qP24IV6krgZBM1JyQc6ng==", - "dependencies": { - "prosemirror-gapcursor": "^1.3.1" - }, + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.220.tgz", + "integrity": "sha512-W5N2Ey+thufUOrs2TFGpEGBGue7ZEhcUXvxcsZlGbrjVa9Y+4rEp68Du4y7yM0hCeSj2GGwiV+uPzkc0CSDE/g==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-hard-break": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.199.tgz", - "integrity": "sha512-DF2wDo/+gSYRhzGowCvZJk3/j/zYJ22BHxZpkAEmLJ69mWSIqZv3S2/brujnNmnji9c3/+JN7ppPSeVykz0b9Q==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.220.tgz", + "integrity": "sha512-oY3454o53YNFbuokzyGzG4PdMHkIYreY3nrALioZ0SwYeoFNcGA6Zcn4rDRfdp+QvbbiHfeBTR/CpWF13HZYTg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-heading": { - "version": "2.0.0-beta.29", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.29.tgz", - "integrity": "sha512-q92jYcsT5bPhvuQaB0h44Z9r+Ii22tDYo082KMVnR4+tknHT/3xx+p4JC8KHjh+/5W8Quyafqy6mS8L8VX0zsQ==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.217.tgz", + "integrity": "sha512-v7G/1R0+qmPm0U2sOCOftqZef5yd89tEg1R+KVF78ucFvuLgH50Pldes8lhHEBlfoRShye2Ht54E6JHDP73J5w==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-history": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.0.0-beta.199.tgz", - "integrity": "sha512-oZMjKHFqqZuUuf0+IG5+OoKw9DIGilG+v8cm2JK9XnxF5CxF6HIXNDWl3552wRIA+Ro7fBRJEJ//hfJzp0Uhjw==", - "dependencies": { - "prosemirror-history": "^1.3.0" - }, + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.0.0-beta.220.tgz", + "integrity": "sha512-qNL2a9UhnlmCs4y2iQYrfeMB8vEX3bHozBJanHu0PWNQJcj90R5xqorBp/bRcqZdi0kuQfxcTnGHtLUpN/U0TA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-horizontal-rule": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.199.tgz", - "integrity": "sha512-ISQndGiC6Y3+Ds3OJHKa2iB7s4FkRQxn8US/Hhj4yK7DOifoykLOrgDghwLu0H0dSM8KNb9caYEtmj64vDogNg==", - "dependencies": { - "prosemirror-state": "^1.4.1" - }, + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.220.tgz", + "integrity": "sha512-XMIs4R+4BoH5LpIxey513mZuus0XLHqjVayqtf03enmjBTLWzkixvvWLPLw4a47FJL5Q8l4REFHxjNifRzOKkg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-image": { - "version": "2.0.0-beta.30", - "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.0.0-beta.30.tgz", - "integrity": "sha512-VhEmgiKkZMiKR7hbpJgIlIUS/QNjSGI5ER7mKDAbuV1IB5yb6nGjZ6o3Exrr2/CaTaW5hQarBC1z2Xgdu05EGg==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.0.0-beta.217.tgz", + "integrity": "sha512-8ibJOicpcqhFeiPLqYOY8SO9sxkz2bemzwhtzRvgkevy/QERe9D0mbAL0qm1gg88LtXy3z2eFUdwD8suq1xsPQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-italic": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.199.tgz", - "integrity": "sha512-jaYJr5ZMxU2swK6h1XJr6Wb1LlWOWbvsX/wo59iZ9KVv1AHiKZlCMcWGThy4aoAs/CUT11pB8qbzyOO163LHZg==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.220.tgz", + "integrity": "sha512-aWAgqoR8fql9fJ7T/ZrEqovkEjZXbUpvlvWEvdBDMG3id8ZTGNDpdDKdvI6J/Rl5ZGPIg1TpHJtd+UixheWQsQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-link": { - "version": "2.0.0-beta.43", - "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.0.0-beta.43.tgz", - "integrity": "sha512-AYueqfTW713KGVfWSWhVbj4ObeWudgawikm3m0uYcKSdsAz/CfEvOD2/NA0uyQzlxmYLA6Pf8HMxoKGN+O4Cmg==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.0.0-beta.217.tgz", + "integrity": "sha512-pC1UnK1OrbW+NDdBdHE8sfmZ+tOpIOsHVAeeX6pg1fkdP/FlbPFFvcvILgJc20dr7DiM6dRqYq16H473G9vyEg==", "dependencies": { - "linkifyjs": "^3.0.5", - "prosemirror-model": "1.18.1", - "prosemirror-state": "1.4.1" + "linkifyjs": "^3.0.5" }, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, - "node_modules/@tiptap/extension-link/node_modules/prosemirror-state": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", - "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", - "dependencies": { - "prosemirror-model": "^1.0.0", - "prosemirror-transform": "^1.0.0" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-list-item": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.199.tgz", - "integrity": "sha512-rzcz5MJgoX1M9M9e1iruyRxcwYyYmdCXsl9gB8hhJYh4R+AW1peRmHJ3vVX5oPZXg/tXOMTv/or2x8v30c9tJw==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.220.tgz", + "integrity": "sha512-+O0ivwxPP2l/m9PAowb2ytDT/cM5kwu0s1W5MUsHPIqf+M6ahnl4ESjhWZfDHUzvjqPq6MTbqoQLHbB1KS/N7w==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-ordered-list": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.199.tgz", - "integrity": "sha512-ciQhBRtNUudQyCgvQKRZ1WbV7Q9IZP82GHEsk+wScZgI0SsrGY8pnfJT7CyF8aPIjkQkccozKVTbyMrjBOqWSw==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.220.tgz", + "integrity": "sha512-j3DmxJfwmNxFfMnvO7glmGlhYeZSIUnRrKnZu2KkpD6OcGJSh9y/yfnYwcuK80XbzEG/jKKIw0M2yRveOvyVwA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-paragraph": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.199.tgz", - "integrity": "sha512-+BoMCaxlsHqw065zTUNd+ywkvFJzNKbTY461/AlKX2dgHeaO8doXHDQK+9icOpibQvrKaMhOJmuBTgGlJlUUgw==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.220.tgz", + "integrity": "sha512-ZGCzNGFYV4wa3l1nXtDIaYp7O6f0DrGTSl3alKkDTQe3SOmzXS2HjgWl9yPw8VXpU9W5mMGhXd+nGn/jUk+f/A==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-strike": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.199.tgz", - "integrity": "sha512-KyN5+d9o9FGvrSiSuh81oo4+XjMDsZVY4UHc9lBY0nAzaGAkJOwkCjk40RfyO5ZJ2GdEEQ6Nh/3YqVMcJTY+rA==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.220.tgz", + "integrity": "sha512-cIM2ma6mzk08pijOn+KS3ZoHWaUVsVT+OF3m6xewjwJdC0ILg9nApEOhPFrhbeDcxcPmJMlgBl/xeUrEu1HQMg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.193" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-table": { - "version": "2.0.0-beta.54", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.0.0-beta.54.tgz", - "integrity": "sha512-KZbocjS9EbWMr/z7U8CUnVhMlV/YEDi7nV1lmrfzmQ6CVIJFQ9FrWCztN1QH3hWnvIcRnR6GM+5VdjFlalsp3A==", - "dependencies": { - "@_ueberdosis/prosemirror-tables": "1.1.3", - "prosemirror-model": "1.18.1", - "prosemirror-state": "1.4.1", - "prosemirror-view": "1.26.2" - }, + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.0.0-beta.217.tgz", + "integrity": "sha512-8PwfNXIRPy1zxZAk0kS+sqFeUE2M6al1y/mA6p0SA9YhSN0iWvjQfmq9Ds52hmRcL2Dv9QmLR97S7WGRmHKcQg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-table-cell": { - "version": "2.0.0-beta.23", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.0.0-beta.23.tgz", - "integrity": "sha512-LTvmAXkbwpLlGhwvVJabOKJbrWZYRp+0DizJaFtBXLSAHqzW9FQjuUhkTokeDRSc+PHMOb0tE1Kz6CRd8onIkA==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.0.0-beta.217.tgz", + "integrity": "sha512-W5UxsZxQdBms916hHp4giXi6AOkwCEfSaTXfi3FQqxcg/EQnmzMNB82/9BcVqBUaoJrx1dIVm4ploIL+GikG/w==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-table-header": { - "version": "2.0.0-beta.25", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.0.0-beta.25.tgz", - "integrity": "sha512-MO9Fa4Ng2sSBAov8cBJR3CxUBSaQQhNY6Dq2h3JINWUs03GPN3iihiVtD5N+0SFs8O4xJ5B8Cifkvd72lSqI3w==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.0.0-beta.217.tgz", + "integrity": "sha512-oahTLhItvoPzCA9RuGLowZ0ZGro+Yn3+1NefXu/yGlp3twKQyhrwOv3+TqZ21L+8uKGOVLfLgPZnF6oNozEdJQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-table-row": { - "version": "2.0.0-beta.22", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.0.0-beta.22.tgz", - "integrity": "sha512-o47oQn3Sv27iIinBf1s1nHoiFLdujNtSkfHhUgUz8zeyXRT2PtKmwSSEglB3c5jGAmt1CLfU8QJrmrV38CwFYw==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.0.0-beta.217.tgz", + "integrity": "sha512-6ie3YtnOliIzER4JtVh0T8HQl3Z2gwTBoCOvqoetsoKIk0zNdsai+ZjVjVN4ZiMLFNYm5xnCUfr83usp9kawhQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, - "node_modules/@tiptap/extension-table/node_modules/prosemirror-state": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", - "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", - "dependencies": { - "prosemirror-model": "^1.0.0", - "prosemirror-transform": "^1.0.0" - } - }, - "node_modules/@tiptap/extension-table/node_modules/prosemirror-view": { - "version": "1.26.2", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.2.tgz", - "integrity": "sha512-CGKw+GadkfSBEwRAJTHCEKJ4DlV6/3IhAdjpwGyZHUHtbP7jX4Ol4zmi7xa2c6GOabDlIJLYXJydoNYLX7lNeQ==", - "dependencies": { - "prosemirror-model": "^1.16.0", - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.1.0" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-text": { - "version": "2.0.0-beta.17", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.0-beta.17.tgz", - "integrity": "sha512-OyKL+pqWJEtjyd9/mrsuY1kZh2b3LWpOQDWKtd4aWR4EA0efmQG+7FPwcIeAVEh7ZoqM+/ABCnPjN6IjzIrSfg==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.0-beta.217.tgz", + "integrity": "sha512-l5PkVhZzVnxbGyMiCMqpLU3ZomJWcZXhuBD0aajry+l85DWc6TK0mhLEAVEU1LoTL8hXFmICoW1KJ1m20Va9dg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-text-align": { - "version": "2.0.0-beta.31", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.0.0-beta.31.tgz", - "integrity": "sha512-gSJqi57piiMPc2r6WEkXv7ZgQIogigsRUhmlnZC/7s3zzOvjXrexWnV0Ctt/9A7BKcM7OHMykpZyoewvk6QRTw==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.0.0-beta.217.tgz", + "integrity": "sha512-oFr1xkUFJKHGJkFA0CDcfkEA4z3jr1umFRkr6qejKiYqwAUdnn5YrtfdGICJzBEa6/1wOCZz0ODzCnv9XmGDkA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209" } }, "node_modules/@tiptap/extension-typography": { - "version": "2.0.0-beta.22", - "resolved": "https://registry.npmjs.org/@tiptap/extension-typography/-/extension-typography-2.0.0-beta.22.tgz", - "integrity": "sha512-sTh1bxRTy2Q0tIGH4pjaadyo7dwoF+5545HiWbG5QsUp27I+FmPfgDYvc7aoPQG/6f5owULL9DD0I7ROYumYTw==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-typography/-/extension-typography-2.0.0-beta.217.tgz", + "integrity": "sha512-xr/KBgdcI3qbTshbY0FCN8BiSRW++MH7yQwvAKlhk47Qt0UcVsRIGc/FfXFD2MhfoZ4vaVrIn5daU4P4p3d+wA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" + "@tiptap/core": "^2.0.0-beta.209" } }, - "node_modules/@tiptap/react": { - "version": "2.0.0-beta.114", - "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.0.0-beta.114.tgz", - "integrity": "sha512-9JbRE+16WM6RxbBxzY74SrJtLodvjeRBnEbWxuhxVgGKxMunRj6r8oED87ODJgqLmkpofwE0KFHTPGdEXfdcKA==", + "node_modules/@tiptap/pm": { + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.0.0-beta.217.tgz", + "integrity": "sha512-krEplJli2BbBB3U2c+cxDPw4mpHsUEwOMJCM3fr1GTVv7qAOvsrWt2ndRPOwraPSvmUF3rw9gvoP86eyK2nDmA==", "dependencies": { - "@tiptap/extension-bubble-menu": "^2.0.0-beta.61", - "@tiptap/extension-floating-menu": "^2.0.0-beta.56", - "prosemirror-view": "1.26.2" + "prosemirror-changeset": "^2.2.0", + "prosemirror-collab": "^1.3.0", + "prosemirror-commands": "^1.3.1", + "prosemirror-dropcursor": "^1.5.0", + "prosemirror-gapcursor": "^1.3.1", + "prosemirror-history": "^1.3.0", + "prosemirror-inputrules": "^1.2.0", + "prosemirror-keymap": "^1.2.0", + "prosemirror-markdown": "^1.10.1", + "prosemirror-menu": "^1.2.1", + "prosemirror-model": "^1.18.1", + "prosemirror-schema-basic": "^1.2.0", + "prosemirror-schema-list": "^1.2.2", + "prosemirror-state": "^1.4.1", + "prosemirror-tables": "^1.3.0", + "prosemirror-trailing-node": "^2.0.2", + "prosemirror-transform": "^1.7.0", + "prosemirror-view": "^1.28.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@tiptap/core": "^2.0.0-beta.209" } }, - "node_modules/@tiptap/react/node_modules/prosemirror-view": { - "version": "1.26.2", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.2.tgz", - "integrity": "sha512-CGKw+GadkfSBEwRAJTHCEKJ4DlV6/3IhAdjpwGyZHUHtbP7jX4Ol4zmi7xa2c6GOabDlIJLYXJydoNYLX7lNeQ==", + "node_modules/@tiptap/react": { + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.0.0-beta.217.tgz", + "integrity": "sha512-aNyvte3fuY97SFilldaz36g2D36D86UZSb/jjxTEAhYc6F4pY8O/4sh20fw6zvoeKAW4DTH/r8v0BRc18+Vm3g==", "dependencies": { - "prosemirror-model": "^1.16.0", - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.1.0" + "@tiptap/extension-bubble-menu": "^2.0.0-beta.217", + "@tiptap/extension-floating-menu": "^2.0.0-beta.217" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0-beta.209", + "@tiptap/pm": "^2.0.0-beta.209", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" } }, "node_modules/@tiptap/starter-kit": { - "version": "2.0.0-beta.191", - "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.191.tgz", - "integrity": "sha512-YRrBCi9W4jiH/xLTJJOCdD7pL4Wb98Ip8qCJ94RElShDj0O1i5tT9wWlgVWoGIU+CRAds5XENRwZ97sJ+YfYyg==", - "dependencies": { - "@tiptap/core": "^2.0.0-beta.182", - "@tiptap/extension-blockquote": "^2.0.0-beta.29", - "@tiptap/extension-bold": "^2.0.0-beta.28", - "@tiptap/extension-bullet-list": "^2.0.0-beta.29", - "@tiptap/extension-code": "^2.0.0-beta.28", - "@tiptap/extension-code-block": "^2.0.0-beta.42", - "@tiptap/extension-document": "^2.0.0-beta.17", - "@tiptap/extension-dropcursor": "^2.0.0-beta.29", - "@tiptap/extension-gapcursor": "^2.0.0-beta.39", - "@tiptap/extension-hard-break": "^2.0.0-beta.33", - "@tiptap/extension-heading": "^2.0.0-beta.29", - "@tiptap/extension-history": "^2.0.0-beta.26", - "@tiptap/extension-horizontal-rule": "^2.0.0-beta.36", - "@tiptap/extension-italic": "^2.0.0-beta.28", - "@tiptap/extension-list-item": "^2.0.0-beta.23", - "@tiptap/extension-ordered-list": "^2.0.0-beta.30", - "@tiptap/extension-paragraph": "^2.0.0-beta.26", - "@tiptap/extension-strike": "^2.0.0-beta.29", - "@tiptap/extension-text": "^2.0.0-beta.17" + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.217.tgz", + "integrity": "sha512-9FPH8lOP0AfRMKm2OUy2OQKEdcIo2ic7MNO2Jtd5vcUz1skfaQPjYLJYHbhkw2iH0ILUOPPbGpZuIlV8uqSu/g==", + "dependencies": { + "@tiptap/core": "^2.0.0-beta.217", + "@tiptap/extension-blockquote": "^2.0.0-beta.217", + "@tiptap/extension-bold": "^2.0.0-beta.217", + "@tiptap/extension-bullet-list": "^2.0.0-beta.217", + "@tiptap/extension-code": "^2.0.0-beta.217", + "@tiptap/extension-code-block": "^2.0.0-beta.217", + "@tiptap/extension-document": "^2.0.0-beta.217", + "@tiptap/extension-dropcursor": "^2.0.0-beta.217", + "@tiptap/extension-gapcursor": "^2.0.0-beta.217", + "@tiptap/extension-hard-break": "^2.0.0-beta.217", + "@tiptap/extension-heading": "^2.0.0-beta.217", + "@tiptap/extension-history": "^2.0.0-beta.217", + "@tiptap/extension-horizontal-rule": "^2.0.0-beta.217", + "@tiptap/extension-italic": "^2.0.0-beta.217", + "@tiptap/extension-list-item": "^2.0.0-beta.217", + "@tiptap/extension-ordered-list": "^2.0.0-beta.217", + "@tiptap/extension-paragraph": "^2.0.0-beta.217", + "@tiptap/extension-strike": "^2.0.0-beta.217", + "@tiptap/extension-text": "^2.0.0-beta.217" }, "funding": { "type": "github", @@ -3113,6 +3124,16 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.5.tgz", "integrity": "sha512-NcKK6Ts+9LqdHJaW6HQmgr7dT/i3GOHG+pt6BiWv++5SnjtRd4NXeiuN2kA153SjhXPR/AhHIPHPbrsbpUVOww==" }, + "node_modules/@types/object.omit": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/object.omit/-/object.omit-3.0.0.tgz", + "integrity": "sha512-I27IoPpH250TUzc9FzXd0P1BV/BMJuzqD3jOz98ehf9dQqGkxlq+hO1bIqZGWqCg5bVOy0g4AUVJtnxe0klDmw==" + }, + "node_modules/@types/object.pick": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/object.pick/-/object.pick-1.3.2.tgz", + "integrity": "sha512-sn7L+qQ6RLPdXRoiaE7bZ/Ek+o4uICma/lBFPyJEKDTPTBP1W8u0c4baj3EiS4DiqLs+Hk+KUGvMVJtAw3ePJg==" + }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -3124,9 +3145,9 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { - "version": "18.0.24", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.24.tgz", - "integrity": "sha512-wRJWT6ouziGUy+9uX0aW4YOJxAY0bG6/AOk5AW5QSvZqI7dk6VBIbXvcVgIw/W5Jrl24f77df98GEKTJGOLx7Q==", + "version": "18.0.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", + "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3154,6 +3175,11 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "node_modules/@types/throttle-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz", + "integrity": "sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -3217,11 +3243,11 @@ } }, "node_modules/adminjs": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/adminjs/-/adminjs-6.5.0.tgz", - "integrity": "sha512-jC55VMdzIg2nzUcLz9uOa30FocrtXjo/CC3/6DmSX7OglORnD6aWgDHe/GrRRsPUrj99sEGGGKZWdgVRjeDSsg==", + "version": "6.8.7", + "resolved": "https://registry.npmjs.org/adminjs/-/adminjs-6.8.7.tgz", + "integrity": "sha512-D3jBb+OgD65qlhrO/dW2AYIiDUTNsJThxYYNiaqufl4yZ9IF+zcYffLRvUJ5EDlv00pIMCM5zff5TMQIRqF8bA==", "dependencies": { - "@adminjs/design-system": "^3.0.3", + "@adminjs/design-system": "^3.1.8", "@babel/core": "^7.10.2", "@babel/parser": "^7.10.2", "@babel/plugin-transform-runtime": "^7.10.1", @@ -3241,7 +3267,7 @@ "axios": "^0.27.2", "babel-plugin-styled-components": "^1.11.1", "commander": "^5.1.0", - "flat": "^4.1.0", + "flat": "^5.0.2", "i18next": "^21.9.2", "lodash": "^4.17.21", "ora": "^5.4.1", @@ -3433,14 +3459,6 @@ "form-data": "^4.0.0" } }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dependencies": { - "object.assign": "^4.1.0" - } - }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -3456,12 +3474,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", - "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dependencies": { "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.2", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" }, "peerDependencies": { @@ -3469,23 +3487,23 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", - "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", - "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.2" + "@babel/helper-define-polyfill-provider": "^0.3.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -3652,9 +3670,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "funding": [ { "type": "opencollective", @@ -3666,10 +3684,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" }, "bin": { "browserslist": "cli.js" @@ -3797,9 +3815,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001377", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001377.tgz", - "integrity": "sha512-I5XeHI1x/mRSGl96LFOaSk528LA/yZG3m3iQgImGujjO8gotd/DL8QaI1R1h1dg5ATeI2jqPblMpKq4Tr5iKfQ==", + "version": "1.0.30001465", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001465.tgz", + "integrity": "sha512-HvjgL3MYAJjceTDCcjRnQGjwUz/5qec9n7JPOzUursUoOTIsYCSDOb1l7RsnZE8mjbxG78zVRCKfrBXyvChBag==", "funding": [ { "type": "opencollective", @@ -3811,6 +3829,17 @@ } ] }, + "node_modules/case-anything": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.10.tgz", + "integrity": "sha512-JczJwVrCP0jPKh05McyVsuOg6AYosrB9XWZKbQzXeDAm2ClE/PJE/BcrrQrVyGYH7Jg8V/LDupmyL4kFlVsVFQ==", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/chai": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", @@ -4093,12 +4122,9 @@ } }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dependencies": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { "version": "0.4.1", @@ -4139,26 +4165,17 @@ "hasInstallScript": true }, "node_modules/core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.1.tgz", + "integrity": "sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==", "dependencies": { - "browserslist": "^4.21.3", - "semver": "7.0.0" + "browserslist": "^4.21.5" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -4172,9 +4189,9 @@ } }, "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -4217,6 +4234,11 @@ "ieee754": "^1.1.13" } }, + "node_modules/crelt": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz", + "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -4264,9 +4286,9 @@ } }, "node_modules/css-to-react-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", - "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", "dependencies": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", @@ -4283,6 +4305,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, + "node_modules/dash-get": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dash-get/-/dash-get-1.0.2.tgz", + "integrity": "sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==" + }, "node_modules/date-fns": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.15.0.tgz", @@ -4341,19 +4368,22 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", "engines": { "node": ">=0.10.0" } }, "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dependencies": { "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { @@ -4476,9 +4506,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.221", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.221.tgz", - "integrity": "sha512-aWg2mYhpxZ6Q6Xvyk7B2ziBca4YqrCDlXzmcD7wuRs65pVEVkMT1u2ifdjpAQais2O2o0rW964ZWWWYRlAL/kw==" + "version": "1.4.328", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.328.tgz", + "integrity": "sha512-DE9tTy2PNmy1v55AZAO542ui+MLC2cvINMK4P2LXGsJdput/ThVG9t+QGecPuAZZSgC8XoI+Jh9M1OG9IoNSCw==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -4527,6 +4557,17 @@ "node": ">=10.0.0" } }, + "node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -5284,12 +5325,9 @@ } }, "node_modules/flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", - "dependencies": { - "is-buffer": "~2.0.3" - }, + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "bin": { "flat": "cli.js" } @@ -5853,32 +5891,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, "node_modules/is-builtin-module": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz", - "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dependencies": { "builtin-modules": "^3.3.0" }, @@ -5901,9 +5917,9 @@ } }, "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dependencies": { "has": "^1.0.3" }, @@ -5925,6 +5941,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -6247,9 +6274,9 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" }, @@ -6356,6 +6383,14 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/linkify-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/linkifyjs": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-3.0.5.tgz", @@ -6606,6 +6641,31 @@ "semver": "bin/semver" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/markdown-it": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -6991,9 +7051,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" }, "node_modules/nodemon": { "version": "2.0.19", @@ -7115,6 +7175,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.omit": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-3.0.0.tgz", + "integrity": "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==", + "dependencies": { + "is-extendable": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -7538,10 +7620,26 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/prosemirror-changeset": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.2.0.tgz", + "integrity": "sha512-QM7ohGtkpVpwVGmFb8wqVhaz9+6IUXcIQBGZ81YNAKYuHiFJ1ShvSzab4pKqTinJhwciZbrtBEk/2WsqSt2PYg==", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.0.tgz", + "integrity": "sha512-+S/IJ69G2cUu2IM5b3PBekuxs94HO1CxJIWOFrLQXUaUDKL/JfBx+QcH31ldBlBXyDEUl+k3Vltfi1E1MKp2mA==", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, "node_modules/prosemirror-commands": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.3.1.tgz", - "integrity": "sha512-XTporPgoECkOQACVw0JTe3RZGi+fls3/byqt+tXwGTkD7qLuB4KdVrJamDMJf4kfKga3uB8hZ+kUUyZ5oWpnfg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.5.1.tgz", + "integrity": "sha512-ga1ga/RkbzxfAvb6iEXYmrEpekn5NCwTb8w1dr/gmhSoaGcQ0VPuCzOn5qDEpC45ql2oDkKoKQbRxLJwKLpMTQ==", "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", @@ -7549,9 +7647,9 @@ } }, "node_modules/prosemirror-dropcursor": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.5.0.tgz", - "integrity": "sha512-vy7i77ddKyXlu8kKBB3nlxLBnsWyKUmQIPB5x8RkYNh01QNp/qqGmdd5yZefJs0s3rtv5r7Izfu2qbtr+tYAMQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.7.1.tgz", + "integrity": "sha512-GmWk9bAwhfHwA8xmJhBFjPcebxUG9zAPYtqpIr7NTDigWZZEJCgUYyUQeqgyscLr8ZHoh9aeprX9kW7BihUT+w==", "dependencies": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0", @@ -7579,23 +7677,60 @@ "rope-sequence": "^1.3.0" } }, - "node_modules/prosemirror-keymap": { + "node_modules/prosemirror-inputrules": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz", - "integrity": "sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.2.0.tgz", + "integrity": "sha512-eAW/M/NTSSzpCOxfR8Abw6OagdG0MiDAiWHQMQveIsZtoKVYzm0AflSPq/ymqJd56/Su1YPbwy9lM13wgHOFmQ==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.1.tgz", + "integrity": "sha512-kVK6WGC+83LZwuSJnuCb9PsADQnFZllt94qPP3Rx/vLcOUV65+IbBeH2nS5cFggPyEVJhGkGrgYFRrG250WhHQ==", "dependencies": { "prosemirror-state": "^1.0.0", "w3c-keyname": "^2.2.0" } }, + "node_modules/prosemirror-markdown": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.10.1.tgz", + "integrity": "sha512-s7iaTLiX+qO5z8kF2NcMmy2T7mIlxzkS4Sp3vTKSYChPtbMpg6YxFkU0Y06rUg2WtKlvBu7v1bXzlGBkfjUWAA==", + "dependencies": { + "markdown-it": "^13.0.1", + "prosemirror-model": "^1.0.0" + } + }, + "node_modules/prosemirror-menu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.1.tgz", + "integrity": "sha512-sBirXxVfHalZO4f1ZS63WzewINK4182+7dOmoMeBkqYO8wqMBvBS7wQuwVOHnkMWPEh0+N0LJ856KYUN+vFkmQ==", + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, "node_modules/prosemirror-model": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz", - "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.19.0.tgz", + "integrity": "sha512-/CvFGJnwc41EJSfDkQLly1cAJJJmBpZwwUJtwZPTjY2RqZJfM8HVbCreOY/jti8wTRbVyjagcylyGoeJH/g/3w==", "dependencies": { "orderedmap": "^2.0.0" } }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.1.tgz", + "integrity": "sha512-vYBdIHsYKSDIqYmPBC7lnwk9DsKn8PnVqK97pMYP5MLEDFqWIX75JiaJTzndBii4bRuNqhC2UfDOfM3FKhlBHg==", + "dependencies": { + "prosemirror-model": "^1.19.0" + } + }, "node_modules/prosemirror-schema-list": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.2.tgz", @@ -7616,18 +7751,57 @@ "prosemirror-view": "^1.27.0" } }, + "node_modules/prosemirror-tables": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.3.2.tgz", + "integrity": "sha512-/9JTeN6s58Zq66HXaxP6uf8PAmc7XXKZFPlOGVtLvxEd6xBP6WtzaJB9wBjiGUzwbdhdMEy7V62yuHqk/3VrnQ==", + "dependencies": { + "prosemirror-keymap": "^1.1.2", + "prosemirror-model": "^1.8.1", + "prosemirror-state": "^1.3.1", + "prosemirror-transform": "^1.2.1", + "prosemirror-view": "^1.13.3" + } + }, + "node_modules/prosemirror-trailing-node": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.3.tgz", + "integrity": "sha512-lGrjMrn97KWkjQSW/FjdvnhJmqFACmQIyr6lKYApvHitDnKsCoZz6XzrHB7RZYHni/0NxQmZ01p/2vyK2SkvaA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@remirror/core-constants": "^2.0.0", + "@remirror/core-helpers": "^2.0.1", + "escape-string-regexp": "^4.0.0" + }, + "peerDependencies": { + "prosemirror-model": "^1", + "prosemirror-state": "^1", + "prosemirror-view": "^1" + } + }, + "node_modules/prosemirror-trailing-node/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prosemirror-transform": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.0.tgz", - "integrity": "sha512-O4T697Cqilw06Zvc3Wm+e237R6eZtJL/xGMliCi+Uo8VL6qHk6afz1qq0zNjT3eZMuYwnP8ZS0+YxX/tfcE9TQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.1.tgz", + "integrity": "sha512-VteoifAfpt46z0yEt6Fc73A5OID9t/y2QIeR5MgxEwTuitadEunD/V0c9jQW8ziT8pbFM54uTzRLJ/nLuQjMxg==", "dependencies": { "prosemirror-model": "^1.0.0" } }, "node_modules/prosemirror-view": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.29.0.tgz", - "integrity": "sha512-bifVd5aD9uCNtpLL1AyhquG/cVbNZSv+ALBxTEGYv51a6OHDhq+aOuzqq4MermNdeBdT+5uyURXCALgzk0EN5g==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.30.1.tgz", + "integrity": "sha512-pZUfr7lICJkEY7XwzldAKrkflZDeIvnbfuu2RIS01N5NwJmR/dfZzDzJRzhb3SM2QtT/bM8b4Nnib8X3MGpAhA==", "dependencies": { "prosemirror-model": "^1.16.0", "prosemirror-state": "^1.0.0", @@ -7763,24 +7937,24 @@ } }, "node_modules/react-currency-input-field": { - "version": "3.6.9", - "resolved": "https://registry.npmjs.org/react-currency-input-field/-/react-currency-input-field-3.6.9.tgz", - "integrity": "sha512-GNqG1Np+dz3VfBhDc9pFPIyar9SgB/fRjIofFSXOoyFNffda5r24n6LSoXfqQ9L7IKRDoUwveMMJV5GCM2eByw==", + "version": "3.6.10", + "resolved": "https://registry.npmjs.org/react-currency-input-field/-/react-currency-input-field-3.6.10.tgz", + "integrity": "sha512-KRAJJaLujarBTLlEVbznsUxQ56+Qyqwoe5w9DnGxmsGnHv4ycQRpRkuuCDfF9BcXHmegzsOXesfIGpW7Cw9mTQ==", "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/react-datepicker": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.8.0.tgz", - "integrity": "sha512-u69zXGHMpxAa4LeYR83vucQoUCJQ6m/WBsSxmUMu/M8ahTSVMMyiyQzauHgZA2NUr9y0FUgOAix71hGYUb6tvg==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.10.0.tgz", + "integrity": "sha512-6IfBCZyWj54ZZGLmEZJ9c4Yph0s9MVfEGDC2evOvf9AmVz+RRcfP2Czqad88Ff9wREbcbqa4dk7IFYeXF1d3Ag==", "dependencies": { "@popperjs/core": "^2.9.2", "classnames": "^2.2.6", "date-fns": "^2.24.0", "prop-types": "^15.7.2", - "react-onclickoutside": "^6.12.0", - "react-popper": "^2.2.5" + "react-onclickoutside": "^6.12.2", + "react-popper": "^2.3.0" }, "peerDependencies": { "react": "^16.9.0 || ^17 || ^18", @@ -7887,9 +8061,9 @@ } }, "node_modules/react-redux": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.4.tgz", - "integrity": "sha512-yMfQ7mX6bWuicz2fids6cR1YT59VTuT8MKyyE310wJQlINKENCeT1UcPdEiX6znI5tF8zXyJ/VYvDgeGuaaNwQ==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", + "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", "dependencies": { "@babel/runtime": "^7.12.1", "@types/hoist-non-react-statics": "^3.3.1", @@ -7925,11 +8099,11 @@ } }, "node_modules/react-router": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.3.tgz", - "integrity": "sha512-BT6DoGn6aV1FVP5yfODMOiieakp3z46P1Fk0RNzJMACzE7C339sFuHebfvWtnB4pzBvXXkHP2vscJzWRuUjTtA==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.9.0.tgz", + "integrity": "sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw==", "dependencies": { - "@remix-run/router": "1.0.3" + "@remix-run/router": "1.4.0" }, "engines": { "node": ">=14" @@ -7939,12 +8113,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.3.tgz", - "integrity": "sha512-MiaYQU8CwVCaOfJdYvt84KQNjT78VF0TJrA17SIQgNHRvLnXDJO6qsFqq8F/zzB1BWZjCFIrQpu4QxcshitziQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.9.0.tgz", + "integrity": "sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q==", "dependencies": { - "@remix-run/router": "1.0.3", - "react-router": "6.4.3" + "@remix-run/router": "1.4.0", + "react-router": "6.9.0" }, "engines": { "node": ">=14" @@ -7955,9 +8129,9 @@ } }, "node_modules/react-select": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.6.0.tgz", - "integrity": "sha512-uUvP/72rA8NGhOL16RVBaeC12Wa4NUE0iXIa6hz0YRno9ZgxTmpuMeKzjR7vHcwmigpVCoe0prP+3NVb6Obq8Q==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.0.tgz", + "integrity": "sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ==", "dependencies": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", @@ -8039,9 +8213,9 @@ } }, "node_modules/redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "dependencies": { "@babel/runtime": "^7.9.2" } @@ -8052,9 +8226,9 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dependencies": { "regenerate": "^1.4.2" }, @@ -8063,14 +8237,14 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "dependencies": { "@babel/runtime": "^7.8.4" } @@ -8103,30 +8277,25 @@ } }, "node_modules/regexpu-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", - "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dependencies": { + "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { "node": ">=4" } }, - "node_modules/regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" - }, "node_modules/regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dependencies": { "jsesc": "~0.5.0" }, @@ -8248,6 +8417,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", "dependencies": { "@babel/code-frame": "^7.10.4", "jest-worker": "^26.2.1", @@ -8565,9 +8735,9 @@ } }, "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "engines": { "node": ">=0.10.0" } @@ -8581,10 +8751,19 @@ "source-map": "^0.6.0" } }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" }, "node_modules/sparse-bitfield": { "version": "3.0.3", @@ -8701,10 +8880,9 @@ } }, "node_modules/styled-components": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", - "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==", - "hasInstallScript": true, + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.8.tgz", + "integrity": "sha512-6jQrlvaJQ16uWVVO0rBfApaTPItkqaG32l3746enNZzpMDxMvzmHzj8rHUg39bvVtom0Y8o8ZzWuchEXKGjVsg==", "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -8956,9 +9134,9 @@ } }, "node_modules/terser": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", - "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.6.tgz", + "integrity": "sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==", "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -8992,6 +9170,14 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, + "node_modules/throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==", + "engines": { + "node": ">=10" + } + }, "node_modules/tiny-invariant": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", @@ -9061,9 +9247,9 @@ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "peer": true }, "node_modules/type-check": { @@ -9109,6 +9295,11 @@ "node": ">= 0.6" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -9161,17 +9352,17 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "engines": { "node": ">=4" } @@ -9185,9 +9376,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "funding": [ { "type": "opencollective", @@ -9578,9 +9769,9 @@ } }, "node_modules/xss": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz", - "integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz", + "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==", "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" @@ -9661,15 +9852,6 @@ "node": ">=10" } }, - "node_modules/yargs-unparser/node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -9711,40 +9893,32 @@ } }, "dependencies": { - "@_ueberdosis/prosemirror-tables": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@_ueberdosis/prosemirror-tables/-/prosemirror-tables-1.1.3.tgz", - "integrity": "sha512-su3pbFi1DT89g6Cuh72TE0MWWKHmWgHcQJ3ODRkm6XfIppWaGpU49t02ur3sgJc7hUhfQXjB93aSkDgOmIii2w==", - "requires": { - "prosemirror-keymap": "^1.1.2", - "prosemirror-model": "^1.8.1", - "prosemirror-state": "^1.3.1", - "prosemirror-transform": "^1.2.1", - "prosemirror-view": "^1.13.3" - } - }, "@adminjs/design-system": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@adminjs/design-system/-/design-system-3.1.0.tgz", - "integrity": "sha512-DRvHWzorYqOwtHr1MSp3WVEs5aLLv9P9msWw0qBdZaRwwb18mAT3z93/FjaclXQ4fBGENRlAdjezS1hewLLjLw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@adminjs/design-system/-/design-system-3.1.8.tgz", + "integrity": "sha512-M0l8NXoHKFoJ9XLv6BkrgRPnE0hCYNYWVNiQKA4qOpzifB2LAPAViqQ36Qyxgz1mL9nnzl7OJpGlb8cHSrIajg==", "requires": { "@carbon/icons-react": "^11.8.0", "@hypnosphi/create-react-context": "^0.3.1", - "@tiptap/extension-character-count": "2.0.0-beta.31", - "@tiptap/extension-code": "2.0.0-beta.28", - "@tiptap/extension-document": "2.0.0-beta.17", - "@tiptap/extension-heading": "2.0.0-beta.29", - "@tiptap/extension-image": "2.0.0-beta.30", - "@tiptap/extension-link": "2.0.0-beta.43", - "@tiptap/extension-table": "2.0.0-beta.54", - "@tiptap/extension-table-cell": "2.0.0-beta.23", - "@tiptap/extension-table-header": "2.0.0-beta.25", - "@tiptap/extension-table-row": "2.0.0-beta.22", - "@tiptap/extension-text": "2.0.0-beta.17", - "@tiptap/extension-text-align": "2.0.0-beta.31", - "@tiptap/extension-typography": "2.0.0-beta.22", - "@tiptap/react": "2.0.0-beta.114", - "@tiptap/starter-kit": "2.0.0-beta.191", + "@tiptap/core": "2.0.0-beta.217", + "@tiptap/extension-bubble-menu": "2.0.0-beta.217", + "@tiptap/extension-character-count": "2.0.0-beta.217", + "@tiptap/extension-code": "2.0.0-beta.217", + "@tiptap/extension-document": "2.0.0-beta.217", + "@tiptap/extension-floating-menu": "2.0.0-beta.217", + "@tiptap/extension-heading": "2.0.0-beta.217", + "@tiptap/extension-image": "2.0.0-beta.217", + "@tiptap/extension-link": "2.0.0-beta.217", + "@tiptap/extension-table": "2.0.0-beta.217", + "@tiptap/extension-table-cell": "2.0.0-beta.217", + "@tiptap/extension-table-header": "2.0.0-beta.217", + "@tiptap/extension-table-row": "2.0.0-beta.217", + "@tiptap/extension-text": "2.0.0-beta.217", + "@tiptap/extension-text-align": "2.0.0-beta.217", + "@tiptap/extension-typography": "2.0.0-beta.217", + "@tiptap/pm": "2.0.0-beta.217", + "@tiptap/react": "2.0.0-beta.217", + "@tiptap/starter-kit": "2.0.0-beta.217", "date-fns": "2.15.0", "jw-paginate": "^1.0.4", "lodash": "^4.17.20", @@ -9759,20 +9933,19 @@ } }, "@adminjs/express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@adminjs/express/-/express-5.0.1.tgz", - "integrity": "sha512-C/5sCizoUN7OvVVANj7hegHJM8vHHfzbcKkCMdbXj8kqdISP4gyTgGFsBXADQTHscLMtn3/MdXv9XfS6iaBU0A==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@adminjs/express/-/express-5.1.0.tgz", + "integrity": "sha512-+mrtDmoAYA9R+/FTYWOLL48g005yrgcAWC2phdwqGzznIxGKSp2YERcfzdTI7Svtnlaal72/QW8Q3OhzJjVLzQ==", "requires": { "path-to-regexp": "^6.2.0" } }, "@adminjs/mongoose": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@adminjs/mongoose/-/mongoose-3.0.1.tgz", - "integrity": "sha512-HX30VuHhE3KH2xL/E9XPe6ohExjfy9GecIMmP7OygUt1dRBJY2u/hRzZ09bf8HsOCRrR5yA8AbuxPBi1hGiQ1A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@adminjs/mongoose/-/mongoose-3.0.3.tgz", + "integrity": "sha512-J/Ogz3oJ2ytOsbeqBpjgIFtiAmGk3MVVfJq2cUidXJ1phrvNHhb7AjiaKd+pcdFcT84COUHaoo6uPYvrLhZEQg==", "requires": { "escape-regexp": "0.0.1", - "flat": "^4.1.0", "lodash": "^4.17.21" } }, @@ -9828,39 +10001,40 @@ } }, "@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==" + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==" }, "@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", + "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", "requires": { - "@ampproject/remapping": "^2.1.0", + "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", + "@babel/generator": "^7.21.0", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.0", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.0", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" } }, "@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", "requires": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.21.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -9894,43 +10068,60 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "requires": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.20.5", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", "semver": "^6.3.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } } }, "@babel/helper-create-class-features-plugin": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", - "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", + "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/helper-split-export-declaration": "^7.18.6" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", - "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", + "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.1.0" + "regexpu-core": "^5.3.1" } }, "@babel/helper-define-polyfill-provider": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", - "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "requires": { "@babel/helper-compilation-targets": "^7.17.7", "@babel/helper-plugin-utils": "^7.16.7", @@ -9954,12 +10145,12 @@ } }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" } }, "@babel/helper-hoist-variables": { @@ -9971,11 +10162,11 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", - "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", + "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", "requires": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.21.0" } }, "@babel/helper-module-imports": { @@ -9987,18 +10178,18 @@ } }, "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" } }, "@babel/helper-optimise-call-expression": { @@ -10010,9 +10201,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==" + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==" }, "@babel/helper-remap-async-to-generator": { "version": "7.18.9", @@ -10026,31 +10217,32 @@ } }, "@babel/helper-replace-supers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", - "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", "requires": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.20.2" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "requires": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" } }, "@babel/helper-split-export-declaration": { @@ -10062,39 +10254,39 @@ } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==" + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" }, "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==" }, "@babel/helper-wrap-function": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz", - "integrity": "sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", "requires": { - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.11", - "@babel/types": "^7.18.10" + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" } }, "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" } }, "@babel/highlight": { @@ -10108,9 +10300,9 @@ } }, "@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==" + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -10121,22 +10313,22 @@ } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", - "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-proposal-optional-chaining": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", - "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", "requires": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" } @@ -10151,12 +10343,12 @@ } }, "@babel/plugin-proposal-class-static-block": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", - "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, @@ -10188,11 +10380,11 @@ } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", - "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", "requires": { - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, @@ -10215,15 +10407,15 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", - "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" + "@babel/plugin-transform-parameters": "^7.20.7" } }, "@babel/plugin-proposal-optional-catch-binding": { @@ -10236,12 +10428,12 @@ } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", - "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, @@ -10255,13 +10447,13 @@ } }, "@babel/plugin-proposal-private-property-in-object": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", - "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", + "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, @@ -10315,11 +10507,11 @@ } }, "@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", - "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-syntax-json-strings": { @@ -10403,29 +10595,29 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", - "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", + "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", - "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", "requires": { "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-remap-async-to-generator": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" } }, "@babel/plugin-transform-block-scoped-functions": { @@ -10437,42 +10629,44 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", - "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", + "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-classes": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", - "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", + "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", - "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", + "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/template": "^7.20.7" } }, "@babel/plugin-transform-destructuring": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", - "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", + "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-dotall-regex": { @@ -10502,11 +10696,11 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", - "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", + "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-function-name": { @@ -10536,36 +10730,33 @@ } }, "@babel/plugin-transform-modules-amd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", - "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", - "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", + "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-simple-access": "^7.20.2" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", - "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", "requires": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" } }, "@babel/plugin-transform-modules-umd": { @@ -10578,12 +10769,12 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", - "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-new-target": { @@ -10604,11 +10795,11 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", + "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-property-literals": { @@ -10628,15 +10819,15 @@ } }, "@babel/plugin-transform-react-jsx": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz", - "integrity": "sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz", + "integrity": "sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.18.10" + "@babel/types": "^7.21.0" } }, "@babel/plugin-transform-react-jsx-development": { @@ -10657,12 +10848,12 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", - "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "regenerator-transform": "^0.15.0" + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" } }, "@babel/plugin-transform-reserved-words": { @@ -10674,15 +10865,15 @@ } }, "@babel/plugin-transform-runtime": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", - "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz", + "integrity": "sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg==", "requires": { "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", + "@babel/helper-plugin-utils": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", "semver": "^6.3.0" } }, @@ -10695,12 +10886,12 @@ } }, "@babel/plugin-transform-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", - "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" } }, "@babel/plugin-transform-sticky-regex": { @@ -10728,13 +10919,13 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.12.tgz", - "integrity": "sha512-2vjjam0cum0miPkenUbQswKowuxs/NjMwIKEq0zwegRxXk12C9YOF9STXnaUptITOtOJHKHpzvvWYOjbm6tc0w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz", + "integrity": "sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-typescript": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" } }, "@babel/plugin-transform-unicode-escapes": { @@ -10764,17 +10955,17 @@ } }, "@babel/preset-env": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", - "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-validator-option": "^7.18.6", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-static-block": "^7.18.6", "@babel/plugin-proposal-dynamic-import": "^7.18.6", @@ -10783,7 +10974,7 @@ "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", "@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-private-methods": "^7.18.6", @@ -10794,7 +10985,7 @@ "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -10807,10 +10998,10 @@ "@babel/plugin-transform-arrow-functions": "^7.18.6", "@babel/plugin-transform-async-to-generator": "^7.18.6", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.18.9", - "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", @@ -10818,30 +11009,30 @@ "@babel/plugin-transform-function-name": "^7.18.9", "@babel/plugin-transform-literals": "^7.18.9", "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-parameters": "^7.20.1", "@babel/plugin-transform-property-literals": "^7.18.6", "@babel/plugin-transform-regenerator": "^7.18.6", "@babel/plugin-transform-reserved-words": "^7.18.6", "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-spread": "^7.19.0", "@babel/plugin-transform-sticky-regex": "^7.18.6", "@babel/plugin-transform-template-literals": "^7.18.9", "@babel/plugin-transform-typeof-symbol": "^7.18.9", "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.18.10", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "core-js-compat": "^3.22.1", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" } }, @@ -10871,19 +11062,19 @@ } }, "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz", + "integrity": "sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-transform-typescript": "^7.21.0" } }, "@babel/register": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.18.9.tgz", - "integrity": "sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.21.0.tgz", + "integrity": "sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw==", "requires": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", @@ -10892,62 +11083,67 @@ "source-map-support": "^0.5.16" } }, + "@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, "@babel/runtime": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", - "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", "requires": { - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.13.11" } }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "requires": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, "@carbon/icon-helpers": { - "version": "10.34.0", - "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.34.0.tgz", - "integrity": "sha512-Ov9EBc1tR/DDrMI0pN1drj2jb27ISmYFBLdDji+aivVJkLPy8R/jikJOsOBgIq2kUjQJYNN199k2acHKjZdYIg==" + "version": "10.39.0", + "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.39.0.tgz", + "integrity": "sha512-iFWIfjKABjusb+gUz6s0FdEBHe8Ms63CKDxozhtiSZ9LfF9X5QQztO8df3szqcNsmw30pYhSnm+zJwifO9tdRw==" }, "@carbon/icons-react": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.10.0.tgz", - "integrity": "sha512-807RWTfbvVzmsDg2DJ4FjwYNbJSgkrEd1Ui8I07YheJVb3sbYGGZMG7aCS0qXVlrQOhB2hggtxSW1w9NksUXNA==", + "version": "11.17.0", + "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.17.0.tgz", + "integrity": "sha512-PheX1aGh12mfybHeLB9w8Y8UzARuqpVanJ3zTDCUQKfPFaOezAt+6E9lqjj5xq3QgpyZcrCEOFiaPV90n3MqGQ==", "requires": { - "@carbon/icon-helpers": "^10.34.0", + "@carbon/icon-helpers": "^10.39.0", "@carbon/telemetry": "0.1.0", "prop-types": "^15.7.2" } @@ -10973,12 +11169,11 @@ } }, "@emotion/babel-plugin": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", - "integrity": "sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==", + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz", + "integrity": "sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==", "requires": { "@babel/helper-module-imports": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.17.12", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.0", "@emotion/memoize": "^0.8.0", @@ -10995,11 +11190,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" } } }, @@ -11034,12 +11224,12 @@ "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" }, "@emotion/react": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz", - "integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==", + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz", + "integrity": "sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==", "requires": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", + "@emotion/babel-plugin": "^11.10.6", "@emotion/cache": "^11.10.5", "@emotion/serialize": "^1.1.1", "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", @@ -11118,22 +11308,22 @@ } }, "@floating-ui/core": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.1.tgz", - "integrity": "sha512-bO37brCPfteXQfFY0DyNDGB3+IMe4j150KFQcgJ5aBP295p9nBGeHEs/p0czrRbtlHq4Px/yoPXO/+dOCcF4uA==" + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.3.tgz", + "integrity": "sha512-upVRtrNZuYNsw+EoxkiBFRPROnU8UTy/u/dZ9U0W14BlemPYODwhhxYXSR2Y9xOnvr1XtptJRWx7gL8Te1qaog==" }, "@floating-ui/dom": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.4.tgz", - "integrity": "sha512-maYJRv+sAXTy4K9mzdv0JPyNW5YPVHrqtY90tEdI6XNpuLOP26Ci2pfwPsKBA/Wh4Z3FX5sUrtUFTdMYj9v+ug==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.4.tgz", + "integrity": "sha512-4+k+BLhtWj+peCU60gp0+rHeR8+Ohqx6kjJf/lHMnJ8JD5Qj6jytcq1+SZzRwD7rvHKRhR7TDiWWddrNrfwQLg==", "requires": { - "@floating-ui/core": "^1.0.1" + "@floating-ui/core": "^1.2.3" } }, "@hello-pangea/dnd": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.0.1.tgz", - "integrity": "sha512-mRWQxRHelK1WAoEYpMfCTOVBIVdmnGXNYmpQUw1LKN/QY6YCNOsUDC4pg3QOtNYRmrxfjCm4HkSgVq5LuD/5ag==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.2.0.tgz", + "integrity": "sha512-inACvMcvvLr34CG0P6+G/3bprVKhwswxjcsFUSJ+fpOGjhvDj9caiA9X3clby0lgJ6/ILIJjyedHZYECB7GAgA==", "requires": { "@babel/runtime": "^7.19.4", "css-box-model": "^1.2.1", @@ -11219,12 +11409,12 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "@jsdevtools/ono": { @@ -11232,6 +11422,11 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, + "@linaria/core": { + "version": "3.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@linaria/core/-/core-3.0.0-beta.13.tgz", + "integrity": "sha512-3zEi5plBCOsEzUneRVuQb+2SAx3qaC1dj0FfFAI6zIJQoDWu0dlSwKijMRack7oO9tUWrchfj3OkKQAd1LBdVg==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -11300,10 +11495,55 @@ "integrity": "sha512-OFp0q4SGrTH0Mruf6oFsHGea58u8vS/iI5+NpYdicaM+7BgqBZH8FFvNZ8rYYLrUO/QRqMq72NpXmxLVNcdmjA==", "requires": {} }, + "@remirror/core-constants": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-2.0.0.tgz", + "integrity": "sha512-vpePPMecHJllBqCWXl6+FIcZqS+tRUM2kSCCKFeEo1H3XUEv3ocijBIPhnlSAa7g6maX+12ATTgxrOsLpWVr2g==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@remirror/core-helpers": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@remirror/core-helpers/-/core-helpers-2.0.1.tgz", + "integrity": "sha512-s8M1pn33aBUhduvD1QR02uUQMegnFkGaTr4c1iBzxTTyg0rbQstzuQ7Q8TkL6n64JtgCdJS9jLz2dONb2meBKQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@linaria/core": "3.0.0-beta.13", + "@remirror/core-constants": "^2.0.0", + "@remirror/types": "^1.0.0", + "@types/object.omit": "^3.0.0", + "@types/object.pick": "^1.3.1", + "@types/throttle-debounce": "^2.1.0", + "case-anything": "^2.1.10", + "dash-get": "^1.0.2", + "deepmerge": "^4.2.2", + "fast-deep-equal": "^3.1.3", + "make-error": "^1.3.6", + "object.omit": "^3.0.0", + "object.pick": "^1.3.0", + "throttle-debounce": "^3.0.1" + } + }, + "@remirror/types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@remirror/types/-/types-1.0.0.tgz", + "integrity": "sha512-7HQbW7k8VxrAtfzs9FxwO6XSDabn8tSFDi1wwzShOnU+cvaYpfxu0ygyTk3TpXsag1hgFKY3ZIlAfB4WVz2LkQ==", + "requires": { + "type-fest": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + } + } + }, "@remix-run/router": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", - "integrity": "sha512-ceuyTSs7PZ/tQqi19YZNBc5X7kj1f8p+4DIyrcIYFY9h+hd1OKm4RqtiWldR9eGEvIiJfsqwM4BsuCtRIuEw6Q==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.4.0.tgz", + "integrity": "sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==" }, "@rollup/plugin-babel": { "version": "5.3.1", @@ -11478,312 +11718,250 @@ } }, "@tiptap/core": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.199.tgz", - "integrity": "sha512-34GaXcBEmNFjW1R7nf1LSmOHo3Q81YjKqvLAXjDLLG7MTx+YTrQ4yWwUvMsZtmi4o/FchUzrs1NVCfr571Zxzg==", - "requires": { - "prosemirror-commands": "^1.3.1", - "prosemirror-keymap": "^1.2.0", - "prosemirror-model": "^1.18.1", - "prosemirror-schema-list": "^1.2.2", - "prosemirror-state": "^1.4.1", - "prosemirror-transform": "^1.7.0", - "prosemirror-view": "^1.28.2" - } + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.217.tgz", + "integrity": "sha512-Vifwcg5SglkVjEmtFbnwHOKWU4UUenOhe7ke5fqGhh7FNfGkccu6sK8W1JTDbG4ARWZ1b/632kQ51YE+WuPe7g==", + "requires": {} }, "@tiptap/extension-blockquote": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.199.tgz", - "integrity": "sha512-BbHKaIkVYgJCV5giJC3/bdXMZWxFylLKiAbOGSGwIsnnS5/oL+V4XN6hqcIDBxlcj3MQ/d9zG0+mvFyjRssAkg==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.220.tgz", + "integrity": "sha512-uE1VRU/doQzXsfsZ/JqsbSbXeZYTJnyQkSfHYA2ZYhbEM2XqDEsYkgcmZEJgunUZJpERf+3ZTfTpqaHq29iMMg==", "requires": {} }, "@tiptap/extension-bold": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.199.tgz", - "integrity": "sha512-l513jgGLmt8C69Yuh5Et7a46Tn8QpW4q1HhZK6ih0ajNT+L5Xk0CSxEK/K5EmHSACPhwqjsJztLpGjAdoOn0mA==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.220.tgz", + "integrity": "sha512-KcEuKI85Drug/cCWbDy+HxhYrD+rLXHEBG10DmKPvgPpKHG/2wOau6LwUwyV4muWR8CR2mIO+mEc3yVBD8nNwQ==", "requires": {} }, "@tiptap/extension-bubble-menu": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.199.tgz", - "integrity": "sha512-T3K8xoDbX6J62lhIUpclQoW/1XFt7yfI5DCoxtVWUeKaF+pG6kdsB3CPG5C/+AQVlz2jSIJmQuPf8RQFpQs+yg==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.217.tgz", + "integrity": "sha512-f52hjIzNbvAJy0P3pTf6sv/65XlMU1LCLXam4VCTkYJ2HHVvW+LZcu+dG1M8YmGkM9l+6dQPs5L2rriAQDXt+w==", "requires": { - "prosemirror-state": "^1.4.1", - "prosemirror-view": "^1.28.2", + "lodash": "^4.17.21", "tippy.js": "^6.3.7" } }, "@tiptap/extension-bullet-list": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.199.tgz", - "integrity": "sha512-gGRQRqdQqCZQstB3ztSy8yzIdm5/5IIYxhCuFNb3Z9c9p/CzyRmaNqa7XkRLrXSajp4lS0OH8RkFUJqL6U+/9w==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.220.tgz", + "integrity": "sha512-QQ/0ZlYy6Hgb+UAc79V+fxvI+AaQf20cbKtBXaR8TIZ0x4FotSma89bKh+CIXMhFiBGXTcYBaYhl7OwACsKtxw==", "requires": {} }, "@tiptap/extension-character-count": { - "version": "2.0.0-beta.31", - "resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.0.0-beta.31.tgz", - "integrity": "sha512-NNA9MN1IjZe+yYQLuYVAg9RNG/3RonYrHiM5mL6vsegd+PF4uMqyZLgsM0/9dMhxh9K/pDPaCRxhuDoZC8V1wA==", - "requires": { - "prosemirror-model": "1.18.1", - "prosemirror-state": "1.4.1" - }, - "dependencies": { - "prosemirror-state": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", - "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", - "requires": { - "prosemirror-model": "^1.0.0", - "prosemirror-transform": "^1.0.0" - } - } - } + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.0.0-beta.217.tgz", + "integrity": "sha512-jj3GzuoUbHcPsRyIkxr/X/+FmnvKeDUtzvWhhpEPOlCf9q5Wjq95VbVYZdNPlEXCNCdMu0ePvyW+v8+kFr+Qlw==", + "requires": {} }, "@tiptap/extension-code": { - "version": "2.0.0-beta.28", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.0.0-beta.28.tgz", - "integrity": "sha512-QPJ2Gwb1+3NgcC1ZIhvVcb+FsnWWDu5VZXTKXM4mz892i9V2x48uHg5anPiUV6pcolXsW1F5VNbXIHGTUUO6CQ==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.0.0-beta.217.tgz", + "integrity": "sha512-IPpuI8MeVX5N0ueWQcIvh2nD+EN6DbFtKSsoU+FgFJ9meOkiEWGibSJ5yFQinPsiE9zrwYIaZzzjhr4oeY2JNA==", "requires": {} }, "@tiptap/extension-code-block": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.199.tgz", - "integrity": "sha512-ZfftYE1kHA2pD46hXDkeYd1vuxp3bJLS854B2yHfw1cp3JVDjMXzm4Mzg7zLfr+YV1dT/N/fUfdCg38fqEUCyA==", - "requires": { - "prosemirror-state": "^1.4.1" - } + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.220.tgz", + "integrity": "sha512-fgA7yTfHqhBtMJF7I9FPJ6UWuZPtxOQiN45Iv9LNmFIB6YRucdpmF+daZ27sElu0a+eICZyXwVn4w4iJphifuw==", + "requires": {} }, "@tiptap/extension-document": { - "version": "2.0.0-beta.17", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.0.0-beta.17.tgz", - "integrity": "sha512-L6sg0FNchbtIpQkCSjMmItVGs3/vep8Fq56WRtDc1wBSGUSmtHaxQG7F2FZLnNIUMuvzVMRD81m2vYG73WkY6A==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.0.0-beta.217.tgz", + "integrity": "sha512-CDCTutbVO1Ub7QUULPCYILl2px48ezCX2kxbspQZzPD7nMoYZNmUMZ8gRJvVDdnSpMNlEl7oNnCAU7uC/lny/A==", "requires": {} }, "@tiptap/extension-dropcursor": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.199.tgz", - "integrity": "sha512-RhdYm0yBJxVLECaHWsZcBIwRJUoUqZ79jvs+kUVodxHW4+IxRAgEA+lImr0GD+kk8aX5Mrk8YhWuUUeu5nzpTg==", - "requires": { - "prosemirror-dropcursor": "1.5.0" - } + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.220.tgz", + "integrity": "sha512-BIaA4Lvb3xL9KFN+K6SO2IHqLO6hDmGN2/rGKHFaU3Eh+oiXM2G73KTSS5KIP1u872zY1RpAtswSc4kjv3cuVw==", + "requires": {} }, "@tiptap/extension-floating-menu": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.199.tgz", - "integrity": "sha512-ELjqnNbxW66uqg54zlP2b4EVYUWvT2WvHmeOXALzoLlNzbqUopIl3XNRsvU2Dv1W88C1UjKgnRZIkHKFE1X3CA==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.217.tgz", + "integrity": "sha512-mP77UrimZo3sdC+Xam0DsPeYfXn29hl4ixEuZeiQQBMqz7g1K9T7vOEsRMQCBVFS7Hsc7MWBqLa1lL1GtyBB9Q==", "requires": { - "prosemirror-state": "^1.4.1", - "prosemirror-view": "^1.28.2", "tippy.js": "^6.3.7" } }, "@tiptap/extension-gapcursor": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.199.tgz", - "integrity": "sha512-0TDpDfDyay+IbD+wJMsBJ2c0Cq0NtllUOxbi0NPjjWW94Jrvs1yqUSzX4Qp9m5MW8qP24IV6krgZBM1JyQc6ng==", - "requires": { - "prosemirror-gapcursor": "^1.3.1" - } + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.220.tgz", + "integrity": "sha512-W5N2Ey+thufUOrs2TFGpEGBGue7ZEhcUXvxcsZlGbrjVa9Y+4rEp68Du4y7yM0hCeSj2GGwiV+uPzkc0CSDE/g==", + "requires": {} }, "@tiptap/extension-hard-break": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.199.tgz", - "integrity": "sha512-DF2wDo/+gSYRhzGowCvZJk3/j/zYJ22BHxZpkAEmLJ69mWSIqZv3S2/brujnNmnji9c3/+JN7ppPSeVykz0b9Q==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.220.tgz", + "integrity": "sha512-oY3454o53YNFbuokzyGzG4PdMHkIYreY3nrALioZ0SwYeoFNcGA6Zcn4rDRfdp+QvbbiHfeBTR/CpWF13HZYTg==", "requires": {} }, "@tiptap/extension-heading": { - "version": "2.0.0-beta.29", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.29.tgz", - "integrity": "sha512-q92jYcsT5bPhvuQaB0h44Z9r+Ii22tDYo082KMVnR4+tknHT/3xx+p4JC8KHjh+/5W8Quyafqy6mS8L8VX0zsQ==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.217.tgz", + "integrity": "sha512-v7G/1R0+qmPm0U2sOCOftqZef5yd89tEg1R+KVF78ucFvuLgH50Pldes8lhHEBlfoRShye2Ht54E6JHDP73J5w==", "requires": {} }, "@tiptap/extension-history": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.0.0-beta.199.tgz", - "integrity": "sha512-oZMjKHFqqZuUuf0+IG5+OoKw9DIGilG+v8cm2JK9XnxF5CxF6HIXNDWl3552wRIA+Ro7fBRJEJ//hfJzp0Uhjw==", - "requires": { - "prosemirror-history": "^1.3.0" - } + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.0.0-beta.220.tgz", + "integrity": "sha512-qNL2a9UhnlmCs4y2iQYrfeMB8vEX3bHozBJanHu0PWNQJcj90R5xqorBp/bRcqZdi0kuQfxcTnGHtLUpN/U0TA==", + "requires": {} }, "@tiptap/extension-horizontal-rule": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.199.tgz", - "integrity": "sha512-ISQndGiC6Y3+Ds3OJHKa2iB7s4FkRQxn8US/Hhj4yK7DOifoykLOrgDghwLu0H0dSM8KNb9caYEtmj64vDogNg==", - "requires": { - "prosemirror-state": "^1.4.1" - } + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.220.tgz", + "integrity": "sha512-XMIs4R+4BoH5LpIxey513mZuus0XLHqjVayqtf03enmjBTLWzkixvvWLPLw4a47FJL5Q8l4REFHxjNifRzOKkg==", + "requires": {} }, "@tiptap/extension-image": { - "version": "2.0.0-beta.30", - "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.0.0-beta.30.tgz", - "integrity": "sha512-VhEmgiKkZMiKR7hbpJgIlIUS/QNjSGI5ER7mKDAbuV1IB5yb6nGjZ6o3Exrr2/CaTaW5hQarBC1z2Xgdu05EGg==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.0.0-beta.217.tgz", + "integrity": "sha512-8ibJOicpcqhFeiPLqYOY8SO9sxkz2bemzwhtzRvgkevy/QERe9D0mbAL0qm1gg88LtXy3z2eFUdwD8suq1xsPQ==", "requires": {} }, "@tiptap/extension-italic": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.199.tgz", - "integrity": "sha512-jaYJr5ZMxU2swK6h1XJr6Wb1LlWOWbvsX/wo59iZ9KVv1AHiKZlCMcWGThy4aoAs/CUT11pB8qbzyOO163LHZg==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.220.tgz", + "integrity": "sha512-aWAgqoR8fql9fJ7T/ZrEqovkEjZXbUpvlvWEvdBDMG3id8ZTGNDpdDKdvI6J/Rl5ZGPIg1TpHJtd+UixheWQsQ==", "requires": {} }, "@tiptap/extension-link": { - "version": "2.0.0-beta.43", - "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.0.0-beta.43.tgz", - "integrity": "sha512-AYueqfTW713KGVfWSWhVbj4ObeWudgawikm3m0uYcKSdsAz/CfEvOD2/NA0uyQzlxmYLA6Pf8HMxoKGN+O4Cmg==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.0.0-beta.217.tgz", + "integrity": "sha512-pC1UnK1OrbW+NDdBdHE8sfmZ+tOpIOsHVAeeX6pg1fkdP/FlbPFFvcvILgJc20dr7DiM6dRqYq16H473G9vyEg==", "requires": { - "linkifyjs": "^3.0.5", - "prosemirror-model": "1.18.1", - "prosemirror-state": "1.4.1" - }, - "dependencies": { - "prosemirror-state": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", - "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", - "requires": { - "prosemirror-model": "^1.0.0", - "prosemirror-transform": "^1.0.0" - } - } + "linkifyjs": "^3.0.5" } }, "@tiptap/extension-list-item": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.199.tgz", - "integrity": "sha512-rzcz5MJgoX1M9M9e1iruyRxcwYyYmdCXsl9gB8hhJYh4R+AW1peRmHJ3vVX5oPZXg/tXOMTv/or2x8v30c9tJw==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.220.tgz", + "integrity": "sha512-+O0ivwxPP2l/m9PAowb2ytDT/cM5kwu0s1W5MUsHPIqf+M6ahnl4ESjhWZfDHUzvjqPq6MTbqoQLHbB1KS/N7w==", "requires": {} }, "@tiptap/extension-ordered-list": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.199.tgz", - "integrity": "sha512-ciQhBRtNUudQyCgvQKRZ1WbV7Q9IZP82GHEsk+wScZgI0SsrGY8pnfJT7CyF8aPIjkQkccozKVTbyMrjBOqWSw==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.220.tgz", + "integrity": "sha512-j3DmxJfwmNxFfMnvO7glmGlhYeZSIUnRrKnZu2KkpD6OcGJSh9y/yfnYwcuK80XbzEG/jKKIw0M2yRveOvyVwA==", "requires": {} }, "@tiptap/extension-paragraph": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.199.tgz", - "integrity": "sha512-+BoMCaxlsHqw065zTUNd+ywkvFJzNKbTY461/AlKX2dgHeaO8doXHDQK+9icOpibQvrKaMhOJmuBTgGlJlUUgw==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.220.tgz", + "integrity": "sha512-ZGCzNGFYV4wa3l1nXtDIaYp7O6f0DrGTSl3alKkDTQe3SOmzXS2HjgWl9yPw8VXpU9W5mMGhXd+nGn/jUk+f/A==", "requires": {} }, "@tiptap/extension-strike": { - "version": "2.0.0-beta.199", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.199.tgz", - "integrity": "sha512-KyN5+d9o9FGvrSiSuh81oo4+XjMDsZVY4UHc9lBY0nAzaGAkJOwkCjk40RfyO5ZJ2GdEEQ6Nh/3YqVMcJTY+rA==", + "version": "2.0.0-beta.220", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.220.tgz", + "integrity": "sha512-cIM2ma6mzk08pijOn+KS3ZoHWaUVsVT+OF3m6xewjwJdC0ILg9nApEOhPFrhbeDcxcPmJMlgBl/xeUrEu1HQMg==", "requires": {} }, "@tiptap/extension-table": { - "version": "2.0.0-beta.54", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.0.0-beta.54.tgz", - "integrity": "sha512-KZbocjS9EbWMr/z7U8CUnVhMlV/YEDi7nV1lmrfzmQ6CVIJFQ9FrWCztN1QH3hWnvIcRnR6GM+5VdjFlalsp3A==", - "requires": { - "@_ueberdosis/prosemirror-tables": "1.1.3", - "prosemirror-model": "1.18.1", - "prosemirror-state": "1.4.1", - "prosemirror-view": "1.26.2" - }, - "dependencies": { - "prosemirror-state": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", - "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", - "requires": { - "prosemirror-model": "^1.0.0", - "prosemirror-transform": "^1.0.0" - } - }, - "prosemirror-view": { - "version": "1.26.2", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.2.tgz", - "integrity": "sha512-CGKw+GadkfSBEwRAJTHCEKJ4DlV6/3IhAdjpwGyZHUHtbP7jX4Ol4zmi7xa2c6GOabDlIJLYXJydoNYLX7lNeQ==", - "requires": { - "prosemirror-model": "^1.16.0", - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.1.0" - } - } - } + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.0.0-beta.217.tgz", + "integrity": "sha512-8PwfNXIRPy1zxZAk0kS+sqFeUE2M6al1y/mA6p0SA9YhSN0iWvjQfmq9Ds52hmRcL2Dv9QmLR97S7WGRmHKcQg==", + "requires": {} }, "@tiptap/extension-table-cell": { - "version": "2.0.0-beta.23", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.0.0-beta.23.tgz", - "integrity": "sha512-LTvmAXkbwpLlGhwvVJabOKJbrWZYRp+0DizJaFtBXLSAHqzW9FQjuUhkTokeDRSc+PHMOb0tE1Kz6CRd8onIkA==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.0.0-beta.217.tgz", + "integrity": "sha512-W5UxsZxQdBms916hHp4giXi6AOkwCEfSaTXfi3FQqxcg/EQnmzMNB82/9BcVqBUaoJrx1dIVm4ploIL+GikG/w==", "requires": {} }, "@tiptap/extension-table-header": { - "version": "2.0.0-beta.25", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.0.0-beta.25.tgz", - "integrity": "sha512-MO9Fa4Ng2sSBAov8cBJR3CxUBSaQQhNY6Dq2h3JINWUs03GPN3iihiVtD5N+0SFs8O4xJ5B8Cifkvd72lSqI3w==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.0.0-beta.217.tgz", + "integrity": "sha512-oahTLhItvoPzCA9RuGLowZ0ZGro+Yn3+1NefXu/yGlp3twKQyhrwOv3+TqZ21L+8uKGOVLfLgPZnF6oNozEdJQ==", "requires": {} }, "@tiptap/extension-table-row": { - "version": "2.0.0-beta.22", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.0.0-beta.22.tgz", - "integrity": "sha512-o47oQn3Sv27iIinBf1s1nHoiFLdujNtSkfHhUgUz8zeyXRT2PtKmwSSEglB3c5jGAmt1CLfU8QJrmrV38CwFYw==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.0.0-beta.217.tgz", + "integrity": "sha512-6ie3YtnOliIzER4JtVh0T8HQl3Z2gwTBoCOvqoetsoKIk0zNdsai+ZjVjVN4ZiMLFNYm5xnCUfr83usp9kawhQ==", "requires": {} }, "@tiptap/extension-text": { - "version": "2.0.0-beta.17", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.0-beta.17.tgz", - "integrity": "sha512-OyKL+pqWJEtjyd9/mrsuY1kZh2b3LWpOQDWKtd4aWR4EA0efmQG+7FPwcIeAVEh7ZoqM+/ABCnPjN6IjzIrSfg==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.0-beta.217.tgz", + "integrity": "sha512-l5PkVhZzVnxbGyMiCMqpLU3ZomJWcZXhuBD0aajry+l85DWc6TK0mhLEAVEU1LoTL8hXFmICoW1KJ1m20Va9dg==", "requires": {} }, "@tiptap/extension-text-align": { - "version": "2.0.0-beta.31", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.0.0-beta.31.tgz", - "integrity": "sha512-gSJqi57piiMPc2r6WEkXv7ZgQIogigsRUhmlnZC/7s3zzOvjXrexWnV0Ctt/9A7BKcM7OHMykpZyoewvk6QRTw==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.0.0-beta.217.tgz", + "integrity": "sha512-oFr1xkUFJKHGJkFA0CDcfkEA4z3jr1umFRkr6qejKiYqwAUdnn5YrtfdGICJzBEa6/1wOCZz0ODzCnv9XmGDkA==", "requires": {} }, "@tiptap/extension-typography": { - "version": "2.0.0-beta.22", - "resolved": "https://registry.npmjs.org/@tiptap/extension-typography/-/extension-typography-2.0.0-beta.22.tgz", - "integrity": "sha512-sTh1bxRTy2Q0tIGH4pjaadyo7dwoF+5545HiWbG5QsUp27I+FmPfgDYvc7aoPQG/6f5owULL9DD0I7ROYumYTw==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/extension-typography/-/extension-typography-2.0.0-beta.217.tgz", + "integrity": "sha512-xr/KBgdcI3qbTshbY0FCN8BiSRW++MH7yQwvAKlhk47Qt0UcVsRIGc/FfXFD2MhfoZ4vaVrIn5daU4P4p3d+wA==", "requires": {} }, + "@tiptap/pm": { + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.0.0-beta.217.tgz", + "integrity": "sha512-krEplJli2BbBB3U2c+cxDPw4mpHsUEwOMJCM3fr1GTVv7qAOvsrWt2ndRPOwraPSvmUF3rw9gvoP86eyK2nDmA==", + "requires": { + "prosemirror-changeset": "^2.2.0", + "prosemirror-collab": "^1.3.0", + "prosemirror-commands": "^1.3.1", + "prosemirror-dropcursor": "^1.5.0", + "prosemirror-gapcursor": "^1.3.1", + "prosemirror-history": "^1.3.0", + "prosemirror-inputrules": "^1.2.0", + "prosemirror-keymap": "^1.2.0", + "prosemirror-markdown": "^1.10.1", + "prosemirror-menu": "^1.2.1", + "prosemirror-model": "^1.18.1", + "prosemirror-schema-basic": "^1.2.0", + "prosemirror-schema-list": "^1.2.2", + "prosemirror-state": "^1.4.1", + "prosemirror-tables": "^1.3.0", + "prosemirror-trailing-node": "^2.0.2", + "prosemirror-transform": "^1.7.0", + "prosemirror-view": "^1.28.2" + } + }, "@tiptap/react": { - "version": "2.0.0-beta.114", - "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.0.0-beta.114.tgz", - "integrity": "sha512-9JbRE+16WM6RxbBxzY74SrJtLodvjeRBnEbWxuhxVgGKxMunRj6r8oED87ODJgqLmkpofwE0KFHTPGdEXfdcKA==", + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.0.0-beta.217.tgz", + "integrity": "sha512-aNyvte3fuY97SFilldaz36g2D36D86UZSb/jjxTEAhYc6F4pY8O/4sh20fw6zvoeKAW4DTH/r8v0BRc18+Vm3g==", "requires": { - "@tiptap/extension-bubble-menu": "^2.0.0-beta.61", - "@tiptap/extension-floating-menu": "^2.0.0-beta.56", - "prosemirror-view": "1.26.2" - }, - "dependencies": { - "prosemirror-view": { - "version": "1.26.2", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.2.tgz", - "integrity": "sha512-CGKw+GadkfSBEwRAJTHCEKJ4DlV6/3IhAdjpwGyZHUHtbP7jX4Ol4zmi7xa2c6GOabDlIJLYXJydoNYLX7lNeQ==", - "requires": { - "prosemirror-model": "^1.16.0", - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.1.0" - } - } + "@tiptap/extension-bubble-menu": "^2.0.0-beta.217", + "@tiptap/extension-floating-menu": "^2.0.0-beta.217" } }, "@tiptap/starter-kit": { - "version": "2.0.0-beta.191", - "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.191.tgz", - "integrity": "sha512-YRrBCi9W4jiH/xLTJJOCdD7pL4Wb98Ip8qCJ94RElShDj0O1i5tT9wWlgVWoGIU+CRAds5XENRwZ97sJ+YfYyg==", - "requires": { - "@tiptap/core": "^2.0.0-beta.182", - "@tiptap/extension-blockquote": "^2.0.0-beta.29", - "@tiptap/extension-bold": "^2.0.0-beta.28", - "@tiptap/extension-bullet-list": "^2.0.0-beta.29", - "@tiptap/extension-code": "^2.0.0-beta.28", - "@tiptap/extension-code-block": "^2.0.0-beta.42", - "@tiptap/extension-document": "^2.0.0-beta.17", - "@tiptap/extension-dropcursor": "^2.0.0-beta.29", - "@tiptap/extension-gapcursor": "^2.0.0-beta.39", - "@tiptap/extension-hard-break": "^2.0.0-beta.33", - "@tiptap/extension-heading": "^2.0.0-beta.29", - "@tiptap/extension-history": "^2.0.0-beta.26", - "@tiptap/extension-horizontal-rule": "^2.0.0-beta.36", - "@tiptap/extension-italic": "^2.0.0-beta.28", - "@tiptap/extension-list-item": "^2.0.0-beta.23", - "@tiptap/extension-ordered-list": "^2.0.0-beta.30", - "@tiptap/extension-paragraph": "^2.0.0-beta.26", - "@tiptap/extension-strike": "^2.0.0-beta.29", - "@tiptap/extension-text": "^2.0.0-beta.17" + "version": "2.0.0-beta.217", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.217.tgz", + "integrity": "sha512-9FPH8lOP0AfRMKm2OUy2OQKEdcIo2ic7MNO2Jtd5vcUz1skfaQPjYLJYHbhkw2iH0ILUOPPbGpZuIlV8uqSu/g==", + "requires": { + "@tiptap/core": "^2.0.0-beta.217", + "@tiptap/extension-blockquote": "^2.0.0-beta.217", + "@tiptap/extension-bold": "^2.0.0-beta.217", + "@tiptap/extension-bullet-list": "^2.0.0-beta.217", + "@tiptap/extension-code": "^2.0.0-beta.217", + "@tiptap/extension-code-block": "^2.0.0-beta.217", + "@tiptap/extension-document": "^2.0.0-beta.217", + "@tiptap/extension-dropcursor": "^2.0.0-beta.217", + "@tiptap/extension-gapcursor": "^2.0.0-beta.217", + "@tiptap/extension-hard-break": "^2.0.0-beta.217", + "@tiptap/extension-heading": "^2.0.0-beta.217", + "@tiptap/extension-history": "^2.0.0-beta.217", + "@tiptap/extension-horizontal-rule": "^2.0.0-beta.217", + "@tiptap/extension-italic": "^2.0.0-beta.217", + "@tiptap/extension-list-item": "^2.0.0-beta.217", + "@tiptap/extension-ordered-list": "^2.0.0-beta.217", + "@tiptap/extension-paragraph": "^2.0.0-beta.217", + "@tiptap/extension-strike": "^2.0.0-beta.217", + "@tiptap/extension-text": "^2.0.0-beta.217" } }, "@types/babel-core": { @@ -11875,6 +12053,16 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.5.tgz", "integrity": "sha512-NcKK6Ts+9LqdHJaW6HQmgr7dT/i3GOHG+pt6BiWv++5SnjtRd4NXeiuN2kA153SjhXPR/AhHIPHPbrsbpUVOww==" }, + "@types/object.omit": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/object.omit/-/object.omit-3.0.0.tgz", + "integrity": "sha512-I27IoPpH250TUzc9FzXd0P1BV/BMJuzqD3jOz98ehf9dQqGkxlq+hO1bIqZGWqCg5bVOy0g4AUVJtnxe0klDmw==" + }, + "@types/object.pick": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/object.pick/-/object.pick-1.3.2.tgz", + "integrity": "sha512-sn7L+qQ6RLPdXRoiaE7bZ/Ek+o4uICma/lBFPyJEKDTPTBP1W8u0c4baj3EiS4DiqLs+Hk+KUGvMVJtAw3ePJg==" + }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -11886,9 +12074,9 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "@types/react": { - "version": "18.0.24", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.24.tgz", - "integrity": "sha512-wRJWT6ouziGUy+9uX0aW4YOJxAY0bG6/AOk5AW5QSvZqI7dk6VBIbXvcVgIw/W5Jrl24f77df98GEKTJGOLx7Q==", + "version": "18.0.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", + "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -11916,6 +12104,11 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "@types/throttle-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz", + "integrity": "sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==" + }, "@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -11968,11 +12161,11 @@ "requires": {} }, "adminjs": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/adminjs/-/adminjs-6.5.0.tgz", - "integrity": "sha512-jC55VMdzIg2nzUcLz9uOa30FocrtXjo/CC3/6DmSX7OglORnD6aWgDHe/GrRRsPUrj99sEGGGKZWdgVRjeDSsg==", + "version": "6.8.7", + "resolved": "https://registry.npmjs.org/adminjs/-/adminjs-6.8.7.tgz", + "integrity": "sha512-D3jBb+OgD65qlhrO/dW2AYIiDUTNsJThxYYNiaqufl4yZ9IF+zcYffLRvUJ5EDlv00pIMCM5zff5TMQIRqF8bA==", "requires": { - "@adminjs/design-system": "^3.0.3", + "@adminjs/design-system": "^3.1.8", "@babel/core": "^7.10.2", "@babel/parser": "^7.10.2", "@babel/plugin-transform-runtime": "^7.10.1", @@ -11992,7 +12185,7 @@ "axios": "^0.27.2", "babel-plugin-styled-components": "^1.11.1", "commander": "^5.1.0", - "flat": "^4.1.0", + "flat": "^5.0.2", "i18next": "^21.9.2", "lodash": "^4.17.21", "ora": "^5.4.1", @@ -12145,14 +12338,6 @@ "form-data": "^4.0.0" } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "requires": { - "object.assign": "^4.1.0" - } - }, "babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -12164,30 +12349,30 @@ } }, "babel-plugin-polyfill-corejs2": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", - "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "requires": { "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.2", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" } }, "babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", - "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", - "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.2" + "@babel/helper-define-polyfill-provider": "^0.3.3" } }, "babel-plugin-styled-components": { @@ -12311,14 +12496,14 @@ "dev": true }, "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" } }, "bson": { @@ -12401,9 +12586,14 @@ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==" }, "caniuse-lite": { - "version": "1.0.30001377", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001377.tgz", - "integrity": "sha512-I5XeHI1x/mRSGl96LFOaSk528LA/yZG3m3iQgImGujjO8gotd/DL8QaI1R1h1dg5ATeI2jqPblMpKq4Tr5iKfQ==" + "version": "1.0.30001465", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001465.tgz", + "integrity": "sha512-HvjgL3MYAJjceTDCcjRnQGjwUz/5qec9n7JPOzUursUoOTIsYCSDOb1l7RsnZE8mjbxG78zVRCKfrBXyvChBag==" + }, + "case-anything": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.10.tgz", + "integrity": "sha512-JczJwVrCP0jPKh05McyVsuOg6AYosrB9XWZKbQzXeDAm2ClE/PJE/BcrrQrVyGYH7Jg8V/LDupmyL4kFlVsVFQ==" }, "chai": { "version": "4.3.6", @@ -12615,12 +12805,9 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "requires": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "cookie": { "version": "0.4.1", @@ -12653,19 +12840,11 @@ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, "core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.1.tgz", + "integrity": "sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==", "requires": { - "browserslist": "^4.21.3", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" - } + "browserslist": "^4.21.5" } }, "cors": { @@ -12678,9 +12857,9 @@ } }, "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "requires": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -12708,6 +12887,11 @@ } } }, + "crelt": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz", + "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" + }, "cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -12740,9 +12924,9 @@ "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==" }, "css-to-react-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", - "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", "requires": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", @@ -12759,6 +12943,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, + "dash-get": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dash-get/-/dash-get-1.0.2.tgz", + "integrity": "sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==" + }, "date-fns": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.15.0.tgz", @@ -12793,14 +12982,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==" }, "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "requires": { "clone": "^1.0.2" } @@ -12894,9 +13083,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.221", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.221.tgz", - "integrity": "sha512-aWg2mYhpxZ6Q6Xvyk7B2ziBca4YqrCDlXzmcD7wuRs65pVEVkMT1u2ifdjpAQais2O2o0rW964ZWWWYRlAL/kw==" + "version": "1.4.328", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.328.tgz", + "integrity": "sha512-DE9tTy2PNmy1v55AZAO542ui+MLC2cvINMK4P2LXGsJdput/ThVG9t+QGecPuAZZSgC8XoI+Jh9M1OG9IoNSCw==" }, "emoji-regex": { "version": "8.0.0", @@ -12936,6 +13125,11 @@ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" }, + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -13515,12 +13709,9 @@ } }, "flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", - "requires": { - "is-buffer": "~2.0.3" - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" }, "flat-cache": { "version": "3.0.4", @@ -13912,15 +14103,10 @@ "has-tostringtag": "^1.0.0" } }, - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" - }, "is-builtin-module": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz", - "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "requires": { "builtin-modules": "^3.3.0" } @@ -13931,9 +14117,9 @@ "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" }, "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "requires": { "has": "^1.0.3" } @@ -13946,6 +14132,14 @@ "has-tostringtag": "^1.0.0" } }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -14165,9 +14359,9 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonwebtoken": { "version": "8.5.1", @@ -14254,6 +14448,14 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "linkify-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "requires": { + "uc.micro": "^1.0.1" + } + }, "linkifyjs": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-3.0.5.tgz", @@ -14463,6 +14665,28 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "markdown-it": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", + "requires": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -14754,9 +14978,9 @@ } }, "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" }, "nodemon": { "version": "2.0.19", @@ -14839,6 +15063,22 @@ "object-keys": "^1.1.1" } }, + "object.omit": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-3.0.0.tgz", + "integrity": "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==", + "requires": { + "is-extendable": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "requires": { + "isobject": "^3.0.1" + } + }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -15145,10 +15385,26 @@ } } }, + "prosemirror-changeset": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.2.0.tgz", + "integrity": "sha512-QM7ohGtkpVpwVGmFb8wqVhaz9+6IUXcIQBGZ81YNAKYuHiFJ1ShvSzab4pKqTinJhwciZbrtBEk/2WsqSt2PYg==", + "requires": { + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-collab": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.0.tgz", + "integrity": "sha512-+S/IJ69G2cUu2IM5b3PBekuxs94HO1CxJIWOFrLQXUaUDKL/JfBx+QcH31ldBlBXyDEUl+k3Vltfi1E1MKp2mA==", + "requires": { + "prosemirror-state": "^1.0.0" + } + }, "prosemirror-commands": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.3.1.tgz", - "integrity": "sha512-XTporPgoECkOQACVw0JTe3RZGi+fls3/byqt+tXwGTkD7qLuB4KdVrJamDMJf4kfKga3uB8hZ+kUUyZ5oWpnfg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.5.1.tgz", + "integrity": "sha512-ga1ga/RkbzxfAvb6iEXYmrEpekn5NCwTb8w1dr/gmhSoaGcQ0VPuCzOn5qDEpC45ql2oDkKoKQbRxLJwKLpMTQ==", "requires": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", @@ -15156,9 +15412,9 @@ } }, "prosemirror-dropcursor": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.5.0.tgz", - "integrity": "sha512-vy7i77ddKyXlu8kKBB3nlxLBnsWyKUmQIPB5x8RkYNh01QNp/qqGmdd5yZefJs0s3rtv5r7Izfu2qbtr+tYAMQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.7.1.tgz", + "integrity": "sha512-GmWk9bAwhfHwA8xmJhBFjPcebxUG9zAPYtqpIr7NTDigWZZEJCgUYyUQeqgyscLr8ZHoh9aeprX9kW7BihUT+w==", "requires": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0", @@ -15186,23 +15442,60 @@ "rope-sequence": "^1.3.0" } }, - "prosemirror-keymap": { + "prosemirror-inputrules": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz", - "integrity": "sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.2.0.tgz", + "integrity": "sha512-eAW/M/NTSSzpCOxfR8Abw6OagdG0MiDAiWHQMQveIsZtoKVYzm0AflSPq/ymqJd56/Su1YPbwy9lM13wgHOFmQ==", + "requires": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-keymap": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.1.tgz", + "integrity": "sha512-kVK6WGC+83LZwuSJnuCb9PsADQnFZllt94qPP3Rx/vLcOUV65+IbBeH2nS5cFggPyEVJhGkGrgYFRrG250WhHQ==", "requires": { "prosemirror-state": "^1.0.0", "w3c-keyname": "^2.2.0" } }, + "prosemirror-markdown": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.10.1.tgz", + "integrity": "sha512-s7iaTLiX+qO5z8kF2NcMmy2T7mIlxzkS4Sp3vTKSYChPtbMpg6YxFkU0Y06rUg2WtKlvBu7v1bXzlGBkfjUWAA==", + "requires": { + "markdown-it": "^13.0.1", + "prosemirror-model": "^1.0.0" + } + }, + "prosemirror-menu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.1.tgz", + "integrity": "sha512-sBirXxVfHalZO4f1ZS63WzewINK4182+7dOmoMeBkqYO8wqMBvBS7wQuwVOHnkMWPEh0+N0LJ856KYUN+vFkmQ==", + "requires": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, "prosemirror-model": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz", - "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.19.0.tgz", + "integrity": "sha512-/CvFGJnwc41EJSfDkQLly1cAJJJmBpZwwUJtwZPTjY2RqZJfM8HVbCreOY/jti8wTRbVyjagcylyGoeJH/g/3w==", "requires": { "orderedmap": "^2.0.0" } }, + "prosemirror-schema-basic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.1.tgz", + "integrity": "sha512-vYBdIHsYKSDIqYmPBC7lnwk9DsKn8PnVqK97pMYP5MLEDFqWIX75JiaJTzndBii4bRuNqhC2UfDOfM3FKhlBHg==", + "requires": { + "prosemirror-model": "^1.19.0" + } + }, "prosemirror-schema-list": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.2.tgz", @@ -15223,18 +15516,48 @@ "prosemirror-view": "^1.27.0" } }, + "prosemirror-tables": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.3.2.tgz", + "integrity": "sha512-/9JTeN6s58Zq66HXaxP6uf8PAmc7XXKZFPlOGVtLvxEd6xBP6WtzaJB9wBjiGUzwbdhdMEy7V62yuHqk/3VrnQ==", + "requires": { + "prosemirror-keymap": "^1.1.2", + "prosemirror-model": "^1.8.1", + "prosemirror-state": "^1.3.1", + "prosemirror-transform": "^1.2.1", + "prosemirror-view": "^1.13.3" + } + }, + "prosemirror-trailing-node": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.3.tgz", + "integrity": "sha512-lGrjMrn97KWkjQSW/FjdvnhJmqFACmQIyr6lKYApvHitDnKsCoZz6XzrHB7RZYHni/0NxQmZ01p/2vyK2SkvaA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@remirror/core-constants": "^2.0.0", + "@remirror/core-helpers": "^2.0.1", + "escape-string-regexp": "^4.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + } + } + }, "prosemirror-transform": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.0.tgz", - "integrity": "sha512-O4T697Cqilw06Zvc3Wm+e237R6eZtJL/xGMliCi+Uo8VL6qHk6afz1qq0zNjT3eZMuYwnP8ZS0+YxX/tfcE9TQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.1.tgz", + "integrity": "sha512-VteoifAfpt46z0yEt6Fc73A5OID9t/y2QIeR5MgxEwTuitadEunD/V0c9jQW8ziT8pbFM54uTzRLJ/nLuQjMxg==", "requires": { "prosemirror-model": "^1.0.0" } }, "prosemirror-view": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.29.0.tgz", - "integrity": "sha512-bifVd5aD9uCNtpLL1AyhquG/cVbNZSv+ALBxTEGYv51a6OHDhq+aOuzqq4MermNdeBdT+5uyURXCALgzk0EN5g==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.30.1.tgz", + "integrity": "sha512-pZUfr7lICJkEY7XwzldAKrkflZDeIvnbfuu2RIS01N5NwJmR/dfZzDzJRzhb3SM2QtT/bM8b4Nnib8X3MGpAhA==", "requires": { "prosemirror-model": "^1.16.0", "prosemirror-state": "^1.0.0", @@ -15328,22 +15651,22 @@ } }, "react-currency-input-field": { - "version": "3.6.9", - "resolved": "https://registry.npmjs.org/react-currency-input-field/-/react-currency-input-field-3.6.9.tgz", - "integrity": "sha512-GNqG1Np+dz3VfBhDc9pFPIyar9SgB/fRjIofFSXOoyFNffda5r24n6LSoXfqQ9L7IKRDoUwveMMJV5GCM2eByw==", + "version": "3.6.10", + "resolved": "https://registry.npmjs.org/react-currency-input-field/-/react-currency-input-field-3.6.10.tgz", + "integrity": "sha512-KRAJJaLujarBTLlEVbznsUxQ56+Qyqwoe5w9DnGxmsGnHv4ycQRpRkuuCDfF9BcXHmegzsOXesfIGpW7Cw9mTQ==", "requires": {} }, "react-datepicker": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.8.0.tgz", - "integrity": "sha512-u69zXGHMpxAa4LeYR83vucQoUCJQ6m/WBsSxmUMu/M8ahTSVMMyiyQzauHgZA2NUr9y0FUgOAix71hGYUb6tvg==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.10.0.tgz", + "integrity": "sha512-6IfBCZyWj54ZZGLmEZJ9c4Yph0s9MVfEGDC2evOvf9AmVz+RRcfP2Czqad88Ff9wREbcbqa4dk7IFYeXF1d3Ag==", "requires": { "@popperjs/core": "^2.9.2", "classnames": "^2.2.6", "date-fns": "^2.24.0", "prop-types": "^15.7.2", - "react-onclickoutside": "^6.12.0", - "react-popper": "^2.2.5" + "react-onclickoutside": "^6.12.2", + "react-popper": "^2.3.0" }, "dependencies": { "date-fns": { @@ -15410,9 +15733,9 @@ } }, "react-redux": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.4.tgz", - "integrity": "sha512-yMfQ7mX6bWuicz2fids6cR1YT59VTuT8MKyyE310wJQlINKENCeT1UcPdEiX6znI5tF8zXyJ/VYvDgeGuaaNwQ==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", + "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", "requires": { "@babel/runtime": "^7.12.1", "@types/hoist-non-react-statics": "^3.3.1", @@ -15423,26 +15746,26 @@ } }, "react-router": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.3.tgz", - "integrity": "sha512-BT6DoGn6aV1FVP5yfODMOiieakp3z46P1Fk0RNzJMACzE7C339sFuHebfvWtnB4pzBvXXkHP2vscJzWRuUjTtA==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.9.0.tgz", + "integrity": "sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw==", "requires": { - "@remix-run/router": "1.0.3" + "@remix-run/router": "1.4.0" } }, "react-router-dom": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.3.tgz", - "integrity": "sha512-MiaYQU8CwVCaOfJdYvt84KQNjT78VF0TJrA17SIQgNHRvLnXDJO6qsFqq8F/zzB1BWZjCFIrQpu4QxcshitziQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.9.0.tgz", + "integrity": "sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q==", "requires": { - "@remix-run/router": "1.0.3", - "react-router": "6.4.3" + "@remix-run/router": "1.4.0", + "react-router": "6.9.0" } }, "react-select": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.6.0.tgz", - "integrity": "sha512-uUvP/72rA8NGhOL16RVBaeC12Wa4NUE0iXIa6hz0YRno9ZgxTmpuMeKzjR7vHcwmigpVCoe0prP+3NVb6Obq8Q==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.0.tgz", + "integrity": "sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ==", "requires": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", @@ -15507,9 +15830,9 @@ } }, "redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "requires": { "@babel/runtime": "^7.9.2" } @@ -15520,22 +15843,22 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "requires": { "regenerate": "^1.4.2" } }, "regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "requires": { "@babel/runtime": "^7.8.4" } @@ -15556,27 +15879,22 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" }, "regexpu-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", - "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "requires": { + "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.0" } }, - "regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" - }, "regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "requires": { "jsesc": "~0.5.0" }, @@ -15917,9 +16235,9 @@ } }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" }, "source-map-support": { "version": "0.5.21", @@ -15928,6 +16246,13 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } } }, "sourcemap-codec": { @@ -16014,9 +16339,9 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, "styled-components": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", - "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==", + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.8.tgz", + "integrity": "sha512-6jQrlvaJQ16uWVVO0rBfApaTPItkqaG32l3746enNZzpMDxMvzmHzj8rHUg39bvVtom0Y8o8ZzWuchEXKGjVsg==", "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -16201,9 +16526,9 @@ } }, "terser": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", - "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.6.tgz", + "integrity": "sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==", "requires": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -16233,6 +16558,11 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, + "throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==" + }, "tiny-invariant": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", @@ -16287,9 +16617,9 @@ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "peer": true }, "type-check": { @@ -16320,6 +16650,11 @@ "mime-types": "~2.1.24" } }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, "uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -16360,14 +16695,14 @@ } }, "unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" }, "unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" }, "unpipe": { "version": "1.0.0", @@ -16375,9 +16710,9 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -16658,9 +16993,9 @@ "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==" }, "xss": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz", - "integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz", + "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==", "requires": { "commander": "^2.20.3", "cssfilter": "0.0.10" @@ -16720,14 +17055,6 @@ "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - } } }, "yocto-queue": { diff --git a/package.json b/package.json index 7d709586..2bb27423 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "description": "KAIST Taxi Party Matching Web Service", "main": "app.js", "dependencies": { - "@adminjs/express": "^5.0.1", - "@adminjs/mongoose": "^3.0.1", - "adminjs": "^6.5.0", + "@adminjs/express": "^5.1.0", + "@adminjs/mongoose": "^3.0.3", + "adminjs": "^6.8.7", "aws-sdk": "^2.1182.0", "axios": "^0.27.2", "connect-mongo": "^4.6.0", diff --git a/src/route/admin.js b/src/route/admin.js index 8085ca3f..759b403f 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -11,7 +11,7 @@ const { adminIPWhitelistModel, } = require("../db/mongo"); -let router = express.Router(); +const router = express.Router(); // Requires admin property of the user to enter admin page. router.use(require("../middleware/authAdmin")); @@ -20,7 +20,7 @@ router.use(require("../middleware/authAdmin")); AdminJS.registerAdapter(AdminJSMongoose); // Create router for admin page -const adminJsOptions = { +const adminJS = new AdminJS({ resources: [ userModel, roomModel, @@ -29,8 +29,7 @@ const adminJsOptions = { reportModel, adminIPWhitelistModel, ], -}; -const adminJs = new AdminJS(adminJsOptions); -router = AdminJSExpress.buildRouter(adminJs, router); +}); +router.use(AdminJSExpress.buildRouter(adminJS)); module.exports = router; From 8c02af5d48cfddee0798169f5d6070c2ed12ce26 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Mon, 13 Mar 2023 21:54:53 +0900 Subject: [PATCH 130/308] Refactor: admin.js --- src/route/admin.js | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/route/admin.js b/src/route/admin.js index 759b403f..7de271d9 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -1,5 +1,6 @@ const express = require("express"); const AdminJS = require("adminjs"); +const { buildFeature } = require("adminjs"); const AdminJSExpress = require("@adminjs/express"); const AdminJSMongoose = require("@adminjs/mongoose"); const { @@ -15,10 +16,33 @@ const router = express.Router(); // Requires admin property of the user to enter admin page. router.use(require("../middleware/authAdmin")); +router.use(require("../middleware/auth")); // Registration of the mongoose adapter AdminJS.registerAdapter(AdminJSMongoose); +const logAction = (actionName) => (response, request, context) => { + console.log(actionName, request.userId); + return response; +}; + +const resourceWrapper = (resource) => ({ + resource, + features: [ + buildFeature({ + actions: ["list", "show", "new", "edit", "delete", "bulkDelete"].reduce( + (before, actionName) => ({ + ...before, + [actionName]: { + after: logAction(actionName), + }, + }), + {} + ), + }), + ], +}); + // Create router for admin page const adminJS = new AdminJS({ resources: [ @@ -28,7 +52,7 @@ const adminJS = new AdminJS({ chatModel, reportModel, adminIPWhitelistModel, - ], + ].map(resourceWrapper), }); router.use(AdminJSExpress.buildRouter(adminJS)); From 1f5f38bf492e0e054d4bbd4405c4ced3beb0dd8b Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Mon, 13 Mar 2023 15:11:01 +0000 Subject: [PATCH 131/308] Docs: reports --- package-lock.json | 70 ++++++++++++++++++++++++++++++++++++ package.json | 1 + src/route/docs.js | 4 +++ src/route/reports.js | 85 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 159 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 7988aef8..7a666fd5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "redis": "^4.2.0", "response-time": "^2.3.2", "socket.io": "^4.3.1", + "swagger-express-validator": "^1.0.2", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^4.6.0", "validator": "^13.7.0", @@ -8871,6 +8872,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-express-validator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/swagger-express-validator/-/swagger-express-validator-1.0.2.tgz", + "integrity": "sha512-uNP3wjzAJTyu0CcXq2O8yJPCDcRLMR/amf0oRkN8syRBqUIQhBcYFpyQC+1PR1TNa8gxkhWdpjaDG3bbCKZ0dA==", + "dependencies": { + "ajv": "^6.10.2", + "debug": "3.2.6", + "lodash": "^4.17.15", + "path-to-regexp": "2.4.0", + "validator": "10.11.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/swagger-express-validator/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/swagger-express-validator/node_modules/path-to-regexp": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", + "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==" + }, + "node_modules/swagger-express-validator/node_modules/validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/swagger-jsdoc": { "version": "6.2.8", "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", @@ -16141,6 +16179,38 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "swagger-express-validator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/swagger-express-validator/-/swagger-express-validator-1.0.2.tgz", + "integrity": "sha512-uNP3wjzAJTyu0CcXq2O8yJPCDcRLMR/amf0oRkN8syRBqUIQhBcYFpyQC+1PR1TNa8gxkhWdpjaDG3bbCKZ0dA==", + "requires": { + "ajv": "^6.10.2", + "debug": "3.2.6", + "lodash": "^4.17.15", + "path-to-regexp": "2.4.0", + "validator": "10.11.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "path-to-regexp": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", + "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==" + }, + "validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" + } + } + }, "swagger-jsdoc": { "version": "6.2.8", "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", diff --git a/package.json b/package.json index 7d709586..b05739ee 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "redis": "^4.2.0", "response-time": "^2.3.2", "socket.io": "^4.3.1", + "swagger-express-validator": "^1.0.2", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^4.6.0", "validator": "^13.7.0", diff --git a/src/route/docs.js b/src/route/docs.js index 3e7ccb51..44c716d4 100644 --- a/src/route/docs.js +++ b/src/route/docs.js @@ -20,6 +20,10 @@ const swaggerSpec = swaggereJsdoc({ name: "logininfo", description: "로그인 정보 제공", }, + { + name: "reports", + description: "사용자 신고 및 신고 기록 조회", + }, ], consumes: ["application/json"], produces: ["application/json"], diff --git a/src/route/reports.js b/src/route/reports.js index e55632e7..9369c273 100644 --- a/src/route/reports.js +++ b/src/route/reports.js @@ -7,7 +7,56 @@ const reportHandlers = require("../service/reports"); // 라우터 접근 시 로그인 필요 router.use(require("../middleware/auth")); - +/** + * @swagger + * /create: + * post: + * tags: [reports] + * summary: 신고 작성 + * description: 주어진 유저를 전달된 사유로 신고함 + * requestBody: + * description: Update an existent user in the store + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/createHandler' + * responses: + * 200: + * description: report successful + * content: + * text/plain: + * schema: + * type: string + * example: report successful + * 500: + * description: internal server error + * content: + * text/plain: + * schema: + * type: string + * example: internal server error + * components: + * schemas: + * createHandler: + * type: object + * required: + * - reportedId + * - type + * - time + * properties: + * reportedId: + * type: string + * pattern: '^[a-fA-F\d]{24}$' + * type: + * type: string + * enum: ["no-settlement", "no-show", "etc-reason"] + * etcDetail: + * type: string + * maxLength: 30 + * time: + * type: string + * format: date-time + */ router.post( "/create", [ @@ -20,6 +69,40 @@ router.post( reportHandlers.createHandler ); +/** + * @swagger + * /searchByUser: + * get: + * tags: [reports] + * summary: 신고 내역 반환 + * description: 로그인된 사용자의 신고한 내역과, 신고받은 내역을 반환한다
+ * 1000개의 limit이 있다. + * responses: + * 200: + * description: 신고된 내역과 신고 받은 내역 + * content: + * application/json: + * schema: + * type: object + * properties: + * reporting: + * type: array + * items: + * type: object + * description: Report + * reported: + * type: array + * items: + * type: object + * description: Report + * 500: + * description: "internal server error" + * content: + * text/plain: + * schema: + * type: string + * example: "internal server error" + */ router.get("/searchByUser", reportHandlers.searchByUserHandler); module.exports = router; From 828ed3ae894178c22c029c64a3653a81e9afca6f Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 14 Mar 2023 02:48:59 +0900 Subject: [PATCH 132/308] Add: adminLogModel --- app.js | 6 +++--- src/db/mongo.js | 13 +++++++++++++ src/middleware/authAdmin.js | 6 ++---- src/middleware/information.js | 5 +++++ src/middleware/timestamp.js | 5 ----- src/modules/logAPIAccess.js | 2 +- src/route/admin.js | 36 ++++++++++++++++++++++++++++++++--- 7 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 src/middleware/information.js delete mode 100644 src/middleware/timestamp.js diff --git a/app.js b/app.js index 634a4fb9..7e89c2be 100644 --- a/app.js +++ b/app.js @@ -21,12 +21,12 @@ const session = require("./src/middleware/session"); app.use(session); app.use(require("cookie-parser")()); +// [Middleware] Timestamp 및 clientIP 확인 +app.use(require("./src/middleware/information")); + // [Middleware] API 접근 기록 및 응답 시간을 http response의 헤더에 기록합니다. app.use(require("response-time")(logAPIAccess)); -// [Middleware] Timestamp 확인 -app.use(require("./src/middleware/timestamp")); - // [Router] admin 페이지는 rate limiting을 적용하지 않습니다. app.use("/admin/logs", require("./src/route/admin.logs")); app.use("/admin", require("./src/route/admin")); diff --git a/src/db/mongo.js b/src/db/mongo.js index 9bd1dbcf..ffc0db94 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -102,6 +102,18 @@ const adminIPWhitelistSchema = Schema({ description: { type: String, default: "" }, // 설명 }); +const adminLogSchema = Schema({ + user: { type: Schema.Types.ObjectId, ref: "User", required: true }, // Log 취급자 User + time: { type: Date, required: true }, // Log 발생 시각 + ip: { type: String, required: true }, // 접속 IP 주소 + target: { type: String, default: "" }, // 처리한 정보주체 정보 + action: { + type: String, + enum: ["create", "read", "update", "delete"], + required: true, + }, // 수행 업무 +}); + const database = mongoose.connection; database.on("error", console.error.bind(console, "mongoose connection error.")); database.on("open", () => { @@ -138,4 +150,5 @@ module.exports = { "AdminIPWhitelist", adminIPWhitelistSchema ), + adminLogModel: mongoose.model("AdminLog", adminLogSchema), }; diff --git a/src/middleware/authAdmin.js b/src/middleware/authAdmin.js index b426135b..1a335b89 100644 --- a/src/middleware/authAdmin.js +++ b/src/middleware/authAdmin.js @@ -15,13 +15,11 @@ const authAdminMiddleware = async (req, res, next) => { if (!user.isAdmin) return res.redirect(frontUrl); // 접속한 IP가 화이트리스트에 있는지 확인 - const clientIP = - req.headers["x-forwarded-for"] || req.connection.remoteAddress; const ipWhitelist = await adminIPWhitelistModel.find({}); - if (!clientIP) return res.redirect(frontUrl); + if (!req.clientIP) return res.redirect(frontUrl); if ( ipWhitelist.length > 0 && - ipWhitelist.map((x) => x.ip).indexOf(clientIP) < 0 + ipWhitelist.map((x) => x.ip).indexOf(req.clientIP) < 0 ) return res.redirect(frontUrl); diff --git a/src/middleware/information.js b/src/middleware/information.js new file mode 100644 index 00000000..f5d5b014 --- /dev/null +++ b/src/middleware/information.js @@ -0,0 +1,5 @@ +module.exports = (req, _, next) => { + req.clientIP = req.headers["x-forwarded-for"] || req.connection.remoteAddress; + req.timestamp = Date.now(); + next(); +}; diff --git a/src/middleware/timestamp.js b/src/middleware/timestamp.js deleted file mode 100644 index 93b897b0..00000000 --- a/src/middleware/timestamp.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = (req, _, next) => { - const currentTime = Date.now(); - req.timestamp = currentTime; - next(); -}; diff --git a/src/modules/logAPIAccess.js b/src/modules/logAPIAccess.js index e0dcf7c6..b61b5572 100644 --- a/src/modules/logAPIAccess.js +++ b/src/modules/logAPIAccess.js @@ -4,7 +4,7 @@ const logAPIAccess = (req, res, time) => { const { method, originalUrl } = req; const { statusCode } = res; const userId = req.session?.loginInfo?.id || "anonymous"; - const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; + const ip = req.clientIP; logger.info( `${userId}(${ip}) "${method} ${originalUrl}" ${statusCode} on ${time}ms` ); diff --git a/src/route/admin.js b/src/route/admin.js index 7de271d9..d31d67a6 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -10,6 +10,7 @@ const { chatModel, reportModel, adminIPWhitelistModel, + adminLogModel, } = require("../db/mongo"); const router = express.Router(); @@ -21,9 +22,37 @@ router.use(require("../middleware/auth")); // Registration of the mongoose adapter AdminJS.registerAdapter(AdminJSMongoose); -const logAction = (actionName) => (response, request, context) => { - console.log(actionName, request.userId); - return response; +const logAction = (actionName) => async (res, req, context) => { + const user = await userModel.findOne({ id: req.userId }); + const modelName = context?.resource?.MongooseModel?.modelName; + const recordLength = `(length = ${context?.records?.length})`; + const recordId = `(_id = ${context?.record?.params?._id})` || recordLength; + const [action, target] = { + list: ["read", `List<${modelName}>${recordLength}`], + show: ["read", `${modelName}${recordId}`], + new: ["create", `${modelName}${recordId}`], + edit: ["update", `${modelName}${recordId}`], + delete: ["delete", `${modelName}${recordId}`], + bulkDelete: ["delete", `${modelName}${recordId}`], + }?.[actionName]; + + if ( + ["new", "edit", "bulkDelete"].includes(actionName) && + req.method !== "post" + ) + return res; + + if (user?._id && action && target) { + const newLog = new adminLogModel({ + user: user._id, // Log 취급자 User + time: req.timestamp, // Log 발생 시각 + ip: req.clientIP, // 접속 IP 주소 + target: target, // 처리한 정보주체 정보 + action, // 수행 업무 + }); + await newLog.save(); + } + return res; }; const resourceWrapper = (resource) => ({ @@ -52,6 +81,7 @@ const adminJS = new AdminJS({ chatModel, reportModel, adminIPWhitelistModel, + adminLogModel, ].map(resourceWrapper), }); router.use(AdminJSExpress.buildRouter(adminJS)); From bcad0f5029e559b453942ab5376e91e20f669a4e Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 14 Mar 2023 10:36:43 +0900 Subject: [PATCH 133/308] Refactor: target --- src/route/admin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/route/admin.js b/src/route/admin.js index d31d67a6..945b8718 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -47,7 +47,7 @@ const logAction = (actionName) => async (res, req, context) => { user: user._id, // Log 취급자 User time: req.timestamp, // Log 발생 시각 ip: req.clientIP, // 접속 IP 주소 - target: target, // 처리한 정보주체 정보 + target, // 처리한 정보주체 정보 action, // 수행 업무 }); await newLog.save(); From 9996a694168b361aba499636303f67bb3e37b640 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 14 Mar 2023 10:41:09 +0900 Subject: [PATCH 134/308] Refactor: admin.js --- src/route/admin.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/route/admin.js b/src/route/admin.js index 945b8718..188013f1 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -22,6 +22,7 @@ router.use(require("../middleware/auth")); // Registration of the mongoose adapter AdminJS.registerAdapter(AdminJSMongoose); +// AdminJS에서 Log 저장을 하는 action const logAction = (actionName) => async (res, req, context) => { const user = await userModel.findOne({ id: req.userId }); const modelName = context?.resource?.MongooseModel?.modelName; @@ -55,6 +56,7 @@ const logAction = (actionName) => async (res, req, context) => { return res; }; +// AdminJS에서 Log 기록을 하도록 action을 수정합니다 const resourceWrapper = (resource) => ({ resource, features: [ From 2948a810be0ca6cbb27dbdbd88d215e7d51d6168 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 14 Mar 2023 12:37:56 +0900 Subject: [PATCH 135/308] Refactor: logAPIAccess --- app.js | 5 ++--- src/modules/logAPIAccess.js | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 7e89c2be..cce0a333 100644 --- a/app.js +++ b/app.js @@ -38,11 +38,10 @@ app.use(require("./src/middleware/limitRate")); app.use("/docs", require("./src/route/docs")); // [Router] APIs -// /rooms/v2에 요청을 보내는 기존 클라이언트 코드 호환성 유지 app.use("/auth", require("./src/route/auth")); -app.use(["/logininfo", "/json/logininfo"], require("./src/route/logininfo")); +app.use("/logininfo", require("./src/route/logininfo")); app.use("/users", require("./src/route/users")); -app.use(["/rooms/v2", "/rooms"], require("./src/route/rooms")); +app.use("/rooms", require("./src/route/rooms")); app.use("/chats", require("./src/route/chats")); app.use("/locations", require("./src/route/locations")); app.use("/reports", require("./src/route/reports")); diff --git a/src/modules/logAPIAccess.js b/src/modules/logAPIAccess.js index b61b5572..cc402a34 100644 --- a/src/modules/logAPIAccess.js +++ b/src/modules/logAPIAccess.js @@ -1,12 +1,11 @@ const logger = require("./logger"); const logAPIAccess = (req, res, time) => { - const { method, originalUrl } = req; + const { method, originalUrl, clientIP } = req; const { statusCode } = res; const userId = req.session?.loginInfo?.id || "anonymous"; - const ip = req.clientIP; logger.info( - `${userId}(${ip}) "${method} ${originalUrl}" ${statusCode} on ${time}ms` + `${userId}(${clientIP}) "${method} ${originalUrl}" ${statusCode} on ${time}ms` ); }; From 71a85b240f32bba2a642bf7cf238546086078325 Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 14 Mar 2023 22:18:45 +0900 Subject: [PATCH 136/308] Add: add notificationOption model --- src/db/mongo.js | 35 ++++++------ src/modules/fcm.js | 26 +++++++-- src/route/chats.socket.js | 12 ++++- src/route/docs/chats.md | 2 +- src/route/notifications.js | 27 ++++++++++ src/service/notifications.js | 100 +++++++++++++++++++++++++++++++++++ src/service/rooms.js | 2 + 7 files changed, 183 insertions(+), 21 deletions(-) create mode 100644 src/route/notifications.js create mode 100644 src/service/notifications.js diff --git a/src/db/mongo.js b/src/db/mongo.js index 1fd927f3..3051af87 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -36,8 +36,23 @@ const participantSchema = Schema({ }, }); -// 각 사용자의 알림 설정 +const deviceTokenSchema = Schema({ + userId: { + type: Schema.Types.ObjectId, + ref: "User", + required: true, + unique: true, + }, + deviceTokens: [{ type: String, required: true }], +}); + +// 각 디바이스의 알림 설정 const notificationOptionSchema = Schema({ + deviceToken: { + type: String, + required: true, + unique: true, + }, chatting: { type: Boolean, default: true, @@ -66,20 +81,6 @@ const notificationOptionSchema = Schema({ }, //광고성 알림 수신 여부 }); -const deviceTokenSchema = Schema({ - userId: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - unique: true, - }, - deviceTokens: [{ type: String, required: true }], - notificationOptions: { - type: notificationOptionSchema, - required: true, - }, -}); - const topicSubscriptionSchema = Schema({ deviceToken: String, topic: String, @@ -174,6 +175,10 @@ mongoose.connect(security.mongo, { module.exports = { userModel: mongoose.model("User", userSchema), deviceTokenModel: mongoose.model("DeviceToken", deviceTokenSchema), + notificationOptionModel: mongoose.model( + "NotificationOption", + notificationOptionSchema + ), topicSubscriptionModel: mongoose.model( "TopicSubscription", topicSubscriptionSchema diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 233b58e2..eb6b8d32 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -1,5 +1,9 @@ const { getMessaging } = require("firebase-admin/messaging"); -const { deviceTokenModel, topicSubscriptionModel } = require("../db/mongo"); +const { + deviceTokenModel, + notificationOptionModel, + topicSubscriptionModel, +} = require("../db/mongo"); const logger = require("../modules/logger"); /** @@ -10,6 +14,7 @@ const logger = require("../modules/logger"); */ const registerDeviceToken = async (userId, deviceToken) => { try { + // 디바이스 토큰을 DB에 추가합니다. const newDeviceToken = await deviceTokenModel.updateOne( { userId, @@ -20,6 +25,14 @@ const registerDeviceToken = async (userId, deviceToken) => { }, { upsert: true, new: true } ); + + // 디바이스 토큰 관련 설정을 DB에 추가합니다. + await notificationOptionModel.updateOne( + { deviceToken }, + { deviceToken }, + { upsert: true, new: true } + ); + return newDeviceToken.deviceTokens; } catch (error) { logger.error(error); @@ -36,6 +49,7 @@ const registerDeviceToken = async (userId, deviceToken) => { */ const unregisterDeviceToken = async (userId, deviceToken) => { try { + // 디바이스 토큰을 DB에서 삭제합니다. const newDeviceToken = await deviceTokenModel.updateOne( { userId, @@ -46,6 +60,12 @@ const unregisterDeviceToken = async (userId, deviceToken) => { }, { upsert: true, new: true } ); + + // 디바이스 토큰 관련 설정을 DB에서 삭제합니다. + await notificationOptionModel.deleteOne({ + deviceToken, + }); + return newDeviceToken.deviceTokens; } catch (error) { logger.error(error); @@ -111,7 +131,7 @@ const getTokensOfUsers = async (userIds, notificationOptions = {}) => { * 주어진 token들에 메시지 알림을 전송합니다. * TODO: 알림 전송 실패한 토큰 삭제하기 * @param {Array} tokens - 메시지 알림을 받을 기기의 deviceToken들로 구성된 Array입니다. - * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. + * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" | "payment" | "settlement" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. * @param {string} body - 보낼 메시지의 본문입니다. * @param {string?} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. @@ -148,7 +168,7 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { /** * 주어진 topic을 구독하고 있는 모든 기기에 메시지 알림을 전송합니다. * @param {string} topic - 메시지 알림을 보낼 기기들이 구독하고 있는 topic입니다. - * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" 입니다. + * @param {string} type - 메시지 유형으로, "text" | "in" | "out" | "s3img" | "payment" | "settlement" 입니다. * @param {string} title - 보낼 메시지의 제목입니다. * @param {string} body - 보낼 메시지의 본문입니다. * @param {string?} icon - 메시지를 보낸 사람의 프로필 사진 주소입니다. diff --git a/src/route/chats.socket.js b/src/route/chats.socket.js index 5aba359b..e90f513d 100644 --- a/src/route/chats.socket.js +++ b/src/route/chats.socket.js @@ -35,12 +35,20 @@ const getMessageBody = (type, nickname, content) => { // TODO: 사용자 언어를 가져올 수 있으면 개선할 수 있다고 생각합니다. const suffix = " 님이 이미지를 전송하였습니다."; return `${nickname} ${suffix}`; - } else { + } else if (type === "in" || type === "out") { // 채팅 메시지 type이 "in"이거나 "out"인 경우 본문은 "${nickname} 님이 입장하였습니다" 또는 "${nickname} 님이 퇴장하였습니다"가 됩니다. // TODO: 사용자 언어를 가져올 수 있으면 개선할 수 있다고 생각합니다. const suffix = type === "in" ? " 님이 입장하였습니다" : "님이 퇴장하였습니다"; return `${nickname} ${suffix}`; + } else if (type === "payment" || type === "settlement") { + // 채팅 메시지 type이 "in"이거나 "out"인 경우 본문은 "${nickname} 님이 결제를 완료하였습니다" 또는 "${nickname} 님이 정산을 완료하였습니다"가 됩니다. + // TODO: 사용자 언어를 가져올 수 있다면 개선할 수 있다고 생각합니다. + const suffix = + type === "payment" + ? " 님이 결제를 완료하였습니다" + : " 님이 정산을 완료하였습니다"; + return `${nickname} ${suffix}`; } }; @@ -50,7 +58,7 @@ const getMessageBody = (type, nickname, content) => { * @param {Server} io - Socket.io 서버 인스턴스입니다. req.app.get("io")를 통해 접근할 수 있습니다. * @param {string} roomId - 채팅 및 채팅 알림을 보낼 방의 ObjectId입니다. * @param {Object} chat - 채팅 메시지 내용입니다. - * @param {string} chat.type - 채팅 메시지의 유형입니다. "text" | "s3img" | "in" | "out" 입니다. + * @param {string} chat.type - 채팅 메시지의 유형입니다. "text" | "s3img" | "in" | "out" | "payment" | "settlement" 입니다. * @param {string} chat.content - 채팅 메시지의 본문입니다. chat.type이 "s3img"인 경우에는 채팅의 objectId입니다. chat.type이 "in"이거나 "out"인 경우 입퇴장한 사용자의 id(!==ObjectId)입니다. * @param {string} chat.authorId - 채팅을 보낸 사용자의 ObjectId입니다. * @param {Date?} chat.time - optional. 채팅 메시지 전송 시각입니다. diff --git a/src/route/docs/chats.md b/src/route/docs/chats.md index 80f5c699..307d2def 100644 --- a/src/route/docs/chats.md +++ b/src/route/docs/chats.md @@ -15,7 +15,7 @@ Taxi의 채팅 기능은 Socket.IO 라이브러리를 이용해 구현되어 있 ```javascript Chat { roomId: ObjectId, //방의 objectId - type: String, // 메시지 종류("text": 일반 메시지, "in": 입장 메시지, "out": 퇴장 메시지", "s3img": S3에 업로드된 이미지 + type: String, // 메시지 종류("text": 일반 메시지, "in": 입장 메시지, "out": 퇴장 메시지, "s3img": S3에 업로드된 이미지, "payment": 결제 메시지, "settlement": 정산 완료 메시지) authorId: ObejctId, //작성자의 objectId content: String, // 메시지 내용(메시지 종류에 따라 포맷이 상이하며, 하단 참조) time: String(ISO 8601), // ex) '2022-01-12T13:58:20.180Z' diff --git a/src/route/notifications.js b/src/route/notifications.js new file mode 100644 index 00000000..23f91d72 --- /dev/null +++ b/src/route/notifications.js @@ -0,0 +1,27 @@ +const express = require("express"); +const router = express.Router(); +const { body } = require("express-validator"); + +const notificationHandlers = require("../service/notifications"); +const validator = require("../middleware/validator"); + +// 라우터 접근 시 로그인 필요 +router.use(require("../middleware/auth")); + +router.get("/options", notificationHandlers.getNotificationOptions); +router.patch( + "/options", + body("deviceToken").isString(), + body("options").isObject(), + body("options.chatting").optional().isBoolean(), + body("options.keywords").optional().isArray(), + body("options.keywords.*").optional().isString(), + body("options.beforeDepart").optional().isBoolean(), + body("options.notice").optional().isBoolean(), + body("options.notice").optional().isBoolean(), + body("options.advertisement").optional().isBoolean(), + validator, + notificationHandlers.changeNotificationOptions +); + +module.exports = router; diff --git a/src/service/notifications.js b/src/service/notifications.js new file mode 100644 index 00000000..21b8e47e --- /dev/null +++ b/src/service/notifications.js @@ -0,0 +1,100 @@ +const { deviceTokenModel } = require("../db/mongo"); + +const changeNotificationOptions = async (req, res) => { + res.status(400).send("Bad request"); +}; + +const getNotificationOptions = async (req, res) => { + res.status(400).send("Bad request"); +}; + +/* +const changeNotificationOption = async (req, res) => { + try { + const { accessToken, deviceToken, options } = req.body; + + const accessTokenStatus = await jwt.verify(accessToken); + + if (!deviceToken) return res.status(400).send("invalid request"); + + if (!options) return res.status(400).send("invalid request"); + + if ( + accessTokenStatus === TOKEN_EXPIRED || + accessTokenStatus === TOKEN_INVALID + ) + return res.status(401).send("unauthorized"); + + notificationOptionModel.updateOne( + { + owner: accessTokenStatus.id, + deviceToken: deviceToken, + }, + { options: options }, + (err, docs) => { + if (err) { + logger.error(err); + res.status(500).send("DB Error"); + } + if (docs.matchedCount == 0) { + res.status(404).send("DeviceToken not found"); + } else { + res.status(200).send("success"); + } + } + ); + } catch (e) { + logger.error(e); + res.status(500).send("server error"); + } +}; + +const getNotificationOption = async (req, res) => { + try { + const { accessToken, deviceToken } = req.body; + + const accessTokenStatus = await jwt.verify(accessToken); + + if (!deviceToken) return res.status(400).send("invalid request"); + + if ( + accessTokenStatus === TOKEN_EXPIRED || + accessTokenStatus === TOKEN_INVALID + ) + return res.status(401).send("unauthorized"); + try { + notificationOptionModel.findOne( + { + owner: accessTokenStatus.id, + deviceToken: deviceToken, + }, + (err, result) => { + try { + if (err) { + res.status(500).send("db error"); + } + if (result) { + res.json(result.options); + } else { + res.status(404).send("deviceToken isn't in DB"); + } + } catch (e) { + logger.error(e); + res.status(500).send("DB Error"); + } + } + ); + } catch (e) { + console.log(e); + } + } catch (e) { + logger.error(e); + res.status(500).send("server error"); + } +}; +*/ + +module.exports = { + getNotificationOptions, + changeNotificationOptions, +}; diff --git a/src/service/rooms.js b/src/service/rooms.js index e8762a73..3a1ecd98 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -448,6 +448,7 @@ const commitPaymentHandler = async (req, res) => { await user.save(); + // 결제 채팅을 보냅니다. await emitChatEvent(req.app.get("io"), roomId, { type: "payment", content: user.id, @@ -512,6 +513,7 @@ const settlementHandler = async (req, res) => { await user.save(); + // 정산 채팅을 보냅니다. await emitChatEvent(req.app.get("io"), roomId, { type: "settlement", content: user.id, From b3ed21db2fb53e4b4f298f5bc8580fbc659e91bf Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 14 Mar 2023 22:41:35 +0900 Subject: [PATCH 137/308] Fix: login.js --- src/auth/login.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/auth/login.js b/src/auth/login.js index 7c939b8f..eac0054a 100644 --- a/src/auth/login.js +++ b/src/auth/login.js @@ -4,7 +4,9 @@ const getLoginInfo = (req) => { if (req.session.loginInfo) { const { id, sid, name, time } = req.session.loginInfo; const timeFlow = Date.now() - time; - if (timeFlow > 14 * 24 * 3600 * 1000) + // if (timeFlow > 14 * 24 * 3600 * 1000) // 14일 + if (timeFlow > 1 * 3600 * 1000) + // 1시간 return { id: undefined, sid: undefined, name: undefined }; else { req.session.loginInfo.time = Date.now(); From 49419e0b41ff967de917e15385f3637bf7d76b1f Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 14 Mar 2023 22:43:58 +0900 Subject: [PATCH 138/308] Fix: login.js --- src/auth/login.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/auth/login.js b/src/auth/login.js index eac0054a..c18228a6 100644 --- a/src/auth/login.js +++ b/src/auth/login.js @@ -4,9 +4,8 @@ const getLoginInfo = (req) => { if (req.session.loginInfo) { const { id, sid, name, time } = req.session.loginInfo; const timeFlow = Date.now() - time; - // if (timeFlow > 14 * 24 * 3600 * 1000) // 14일 - if (timeFlow > 1 * 3600 * 1000) - // 1시간 + // if (timeFlow > 14 * 24 * 3600 * 1000/* 14일 */) + if (timeFlow > 1 * 3600 * 1000 /* 1시간 */) return { id: undefined, sid: undefined, name: undefined }; else { req.session.loginInfo.time = Date.now(); From 02e4a3ae02738614a062643565d5427b8a66d338 Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 14 Mar 2023 22:56:32 +0900 Subject: [PATCH 139/308] Add: add notificationOptions --- src/route/notifications.js | 8 +- src/service/notifications.js | 150 +++++++++++++++++------------------ 2 files changed, 77 insertions(+), 81 deletions(-) diff --git a/src/route/notifications.js b/src/route/notifications.js index 23f91d72..97895d08 100644 --- a/src/route/notifications.js +++ b/src/route/notifications.js @@ -8,7 +8,12 @@ const validator = require("../middleware/validator"); // 라우터 접근 시 로그인 필요 router.use(require("../middleware/auth")); -router.get("/options", notificationHandlers.getNotificationOptions); +router.get( + "/options", + body("deviceToken").isString(), + notificationHandlers.getNotificationOptions +); + router.patch( "/options", body("deviceToken").isString(), @@ -18,7 +23,6 @@ router.patch( body("options.keywords.*").optional().isString(), body("options.beforeDepart").optional().isBoolean(), body("options.notice").optional().isBoolean(), - body("options.notice").optional().isBoolean(), body("options.advertisement").optional().isBoolean(), validator, notificationHandlers.changeNotificationOptions diff --git a/src/service/notifications.js b/src/service/notifications.js index 21b8e47e..cad2dbc8 100644 --- a/src/service/notifications.js +++ b/src/service/notifications.js @@ -1,98 +1,90 @@ -const { deviceTokenModel } = require("../db/mongo"); - -const changeNotificationOptions = async (req, res) => { - res.status(400).send("Bad request"); -}; +const { notificationOptionModel } = require("../db/mongo"); +const logger = require("../modules/logger"); const getNotificationOptions = async (req, res) => { - res.status(400).send("Bad request"); -}; - -/* -const changeNotificationOption = async (req, res) => { try { - const { accessToken, deviceToken, options } = req.body; + const { deviceToken } = req.body; + if (!deviceToken) { + return res + .status(400) + .send("Notification/getNotificationOptions: deviceToken not found"); + } - const accessTokenStatus = await jwt.verify(accessToken); + // deviceToken에 대응되는 알림 설정을 찾아 반환합니다. + const notificationOptions = await notificationOptionModel + .findOne( + { + deviceToken, + }, + "-_id chatting keywords beforeDepart notice advertisement" + ) + .lean(); + if (!notificationOptions) { + return res + .status(400) + .send("Notificaiton/getNotificationOptions: deviceToken not found"); + } + res.status(200).json(notificationOptions); + } catch (err) { + logger.error(err); + return res + .status(500) + .send("Notification/getNotificationOptions: internal server error"); + } +}; - if (!deviceToken) return res.status(400).send("invalid request"); +const changeNotificationOptions = async (req, res) => { + try { + const { deviceToken, options } = req.body; - if (!options) return res.status(400).send("invalid request"); + if (!deviceToken) { + return res + .status(400) + .send("Notification/changeNotificationOptions: deviceToken not found"); + } - if ( - accessTokenStatus === TOKEN_EXPIRED || - accessTokenStatus === TOKEN_INVALID - ) - return res.status(401).send("unauthorized"); + const newOptions = {}; + const booleanFields = [ + "chatting", + "beforeDepart", + "notice", + "advertisement", + ]; + booleanFields.map((field) => { + if (options[field] === true || options[field] === false) { + newOptions[field] = options[field]; + } + }); + if (options.keywords) { + newOptions.keywords = options.keyword; + } - notificationOptionModel.updateOne( + const updatedNotificationOptions = await notificationOptionModel.updateOne( + { + deviceToken, + }, { - owner: accessTokenStatus.id, - deviceToken: deviceToken, + deviceToken, + newOptions, }, - { options: options }, - (err, docs) => { - if (err) { - logger.error(err); - res.status(500).send("DB Error"); - } - if (docs.matchedCount == 0) { - res.status(404).send("DeviceToken not found"); - } else { - res.status(200).send("success"); - } + { + new: true, } ); - } catch (e) { - logger.error(e); - res.status(500).send("server error"); - } -}; -const getNotificationOption = async (req, res) => { - try { - const { accessToken, deviceToken } = req.body; - - const accessTokenStatus = await jwt.verify(accessToken); - - if (!deviceToken) return res.status(400).send("invalid request"); + if (!updatedNotificationOptions) + res + .status(400) + .send("Notification/changeNotificationOptions: deviceToken not found"); - if ( - accessTokenStatus === TOKEN_EXPIRED || - accessTokenStatus === TOKEN_INVALID - ) - return res.status(401).send("unauthorized"); - try { - notificationOptionModel.findOne( - { - owner: accessTokenStatus.id, - deviceToken: deviceToken, - }, - (err, result) => { - try { - if (err) { - res.status(500).send("db error"); - } - if (result) { - res.json(result.options); - } else { - res.status(404).send("deviceToken isn't in DB"); - } - } catch (e) { - logger.error(e); - res.status(500).send("DB Error"); - } - } - ); - } catch (e) { - console.log(e); - } - } catch (e) { - logger.error(e); - res.status(500).send("server error"); + res.status(200).json(updatedNotificationOptions); + } catch (err) { + logger.error(err); + return res + .status(500) + .send("Notification/changeNotificationOptions: internal server error"); } }; -*/ module.exports = { getNotificationOptions, From bce2ce13ea8eabbbd7639fb151b27bb9a800ab95 Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 14 Mar 2023 22:57:22 +0900 Subject: [PATCH 140/308] Add: add prototype of token clearance --- src/modules/fcm.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index eb6b8d32..20f6a85e 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -73,6 +73,16 @@ const unregisterDeviceToken = async (userId, deviceToken) => { } }; +/** + * 메시지 전송에 실패한 deviceToken을 DB에서 삭제합니다. + * @param {Array} deviceTokens - 사용자의 ObjectId입니다. + * @param {Array} fcmResponses - 등록하려는 FCM device token입니다. + * @return {Promise} 해당 토큰들을 DB에서 삭제하는 데 성공했으면 true, 아니면 false를 반환합니다. + */ +const removeExpiredTokens = async (deviceTokens, fcmResponses) => { + return false; +}; + /** * 사용자의 FCM device token이 현재 사용 가능한지 검증합니다. * @summary 해당 디바이스에 dry-run 방식으로 메시지 전송을 시험함으로써 해당 deviceToken이 사용 가능한지 검증합니다. dry-run 시 FCM 서버에는 메시지 전송 요청이 전송되지만, 실제 기기에는 알림이 전송되지 않습니다. @@ -156,8 +166,11 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { }, }, }; - const { failureCount } = await getMessaging().sendMulticast(message); + const { Responses, failureCount } = await getMessaging().sendMulticast( + message + ); logger.info(`Notification sent failed for ${failureCount} devices`); + await removeExpiredTokens(tokens, Responses); return failureCount; } catch (error) { logger.error(error); From b5d2dc857af9a2977bbdcff2623f9f43c893b0d2 Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 14 Mar 2023 23:15:29 +0900 Subject: [PATCH 141/308] Add: implement removal of expired tokens --- src/modules/fcm.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 20f6a85e..bf056096 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -47,15 +47,12 @@ const registerDeviceToken = async (userId, deviceToken) => { * @param {string} deviceToken - 삭제하려는 FCM device token입니다. * @return {Promise>} 변경된 사용자의 deviceToken의 목록 Array를 반환합니다. 오류가 발생하면 빈 배열을 반환합니다. */ -const unregisterDeviceToken = async (userId, deviceToken) => { +const unregisterDeviceToken = async (deviceToken) => { try { // 디바이스 토큰을 DB에서 삭제합니다. const newDeviceToken = await deviceTokenModel.updateOne( + {}, { - userId, - }, - { - userId, $pull: { deviceTokens: deviceToken }, }, { upsert: true, new: true } @@ -76,11 +73,23 @@ const unregisterDeviceToken = async (userId, deviceToken) => { /** * 메시지 전송에 실패한 deviceToken을 DB에서 삭제합니다. * @param {Array} deviceTokens - 사용자의 ObjectId입니다. - * @param {Array} fcmResponses - 등록하려는 FCM device token입니다. - * @return {Promise} 해당 토큰들을 DB에서 삭제하는 데 성공했으면 true, 아니면 false를 반환합니다. + * @param {Array} fcmResponses - 등록하려는 FCM device token입니다. + * @return {Promise>} 각각의 토큰들의 삭제 성공 여부가 저장된 Array를 반환합니다. 해당 토큰을 DB에서 삭제하는 데 성공했으면 true, 아니면 false가 포함됩니다. */ const removeExpiredTokens = async (deviceTokens, fcmResponses) => { - return false; + const removalResults = await Promise.all( + deviceTokens.map(async (deviceToken, index) => { + try { + const fcmResponse = fcmResponses[index]; + if (!fcmResponse.Exception) return; + await unregisterDeviceToken(deviceToken); + return true; + } catch (err) { + return false; + } + }) + ); + return removalResults; }; /** From b982752f67531e2140a1b23ebd11128695b12801 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 15 Mar 2023 04:11:51 +0900 Subject: [PATCH 142/308] Fix: remove debugging console.log code --- src/modules/fcm.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index bf056096..edb9bd4a 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -126,20 +126,10 @@ const getTokensOfUsers = async (userIds, notificationOptions = {}) => { userIds.map(async (userId) => { const query = { userId }; if (notificationOptions) query.notificationOptions = notificationOptions; - const deviceToken = await deviceTokenModel.findOne({ query }); + const deviceToken = await deviceTokenModel.findOne(query); return deviceToken?.deviceTokens || new Array(); }) ); - logger.info( - await Promise.all( - userIds.map(async (userId) => { - const deviceToken = await deviceTokenModel.findOne({ - userId, - }); - return deviceToken?.deviceTokens || new Array(); - }) - ) - ); return deviceTokensOfUsers.reduce( (arrayA, arrayB) => arrayA.concat(arrayB), new Array() From 4787710e61e1ced2ec19d4fe27ae024cbf3dd505 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 15 Mar 2023 04:51:10 +0900 Subject: [PATCH 143/308] Fix: fix invalid tokens not removed by wrong query --- src/modules/fcm.js | 38 +++++++++++++++++++++++++------------ src/service/auth.js | 5 ++--- src/service/auth.mobile.js | 2 +- src/service/auth.replace.js | 5 +---- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index edb9bd4a..e337c75c 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -45,13 +45,13 @@ const registerDeviceToken = async (userId, deviceToken) => { * 사용자의 ObjectId와 FCM device token이 주어졌을 때, 해당 사용자의 해당 deviceToken을 DB에서 삭제합니다. * @param {string} userId - 사용자의 ObjectId입니다. * @param {string} deviceToken - 삭제하려는 FCM device token입니다. - * @return {Promise>} 변경된 사용자의 deviceToken의 목록 Array를 반환합니다. 오류가 발생하면 빈 배열을 반환합니다. + * @return {Promise} 해당 deviceToken을 가진 모든 사용자로부터 해당 deviceToken을 삭제하는 데 성공하면 true, 하나 이상의 사용자에게서 해당 deviceToken을 삭제하는 데 실패하면 false를 반환합니다. 삭제할 deviceToken이 존재하지 않는 경우에는 true를 반환합니다. */ const unregisterDeviceToken = async (deviceToken) => { try { // 디바이스 토큰을 DB에서 삭제합니다. - const newDeviceToken = await deviceTokenModel.updateOne( - {}, + const { matchedCount, modifiedCount } = await deviceTokenModel.updateMany( + { deviceTokens: deviceToken }, { $pull: { deviceTokens: deviceToken }, }, @@ -63,10 +63,10 @@ const unregisterDeviceToken = async (deviceToken) => { deviceToken, }); - return newDeviceToken.deviceTokens; + return matchedCount === modifiedCount; } catch (error) { logger.error(error); - return new Array(); + return false; } }; @@ -80,15 +80,24 @@ const removeExpiredTokens = async (deviceTokens, fcmResponses) => { const removalResults = await Promise.all( deviceTokens.map(async (deviceToken, index) => { try { - const fcmResponse = fcmResponses[index]; - if (!fcmResponse.Exception) return; - await unregisterDeviceToken(deviceToken); - return true; + // FCM device token이 유효하지 않아 메시지 전송에 실패한 경우, 해당 device token을 DB에서 삭제합니다. + if ( + fcmResponses[index].error.code === + "messaging/registration-token-not-registered" + ) { + await unregisterDeviceToken(deviceToken); + return true; + } + return false; } catch (err) { return false; } }) ); + const removedTokenCount = removalResults.filter((result) => result).length; + logger.info( + `${removedTokenCount} deviceTokens were removed from the database.` + ); return removalResults; }; @@ -165,11 +174,16 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { }, }, }; - const { Responses, failureCount } = await getMessaging().sendMulticast( + const { responses, failureCount } = await getMessaging().sendMulticast( message ); - logger.info(`Notification sent failed for ${failureCount} devices`); - await removeExpiredTokens(tokens, Responses); + + // 메시지 전송에 실패한 기기가 존재할 경우, 해당 기기의 deviceToken을 DB에서 삭제합니다. + if (failureCount) { + logger.info(`Notification sent failed for ${failureCount} devices`); + await removeExpiredTokens(tokens, responses); + } + return failureCount; } catch (error) { logger.error(error); diff --git a/src/service/auth.js b/src/service/auth.js index 329ec082..0c76b020 100644 --- a/src/service/auth.js +++ b/src/service/auth.js @@ -163,13 +163,12 @@ const createNewTokenHandler = (req, res, userData) => { const logoutHandler = async (req, res) => { try { - const { id, sid } = getLoginInfo(req); + const { sid } = getLoginInfo(req); // DB에서 deviceToken 레코드를 삭제합니다. const deviceToken = req.session?.deviceToken; if (deviceToken) { - const user = await userModel.findOne({ id }, "_id"); - await unregisterDeviceToken(user._id, deviceToken); + await unregisterDeviceToken(deviceToken); } const redirectUrl = security.frontUrl + "/login"; diff --git a/src/service/auth.mobile.js b/src/service/auth.mobile.js index 7ff8b279..e4e07b2c 100644 --- a/src/service/auth.mobile.js +++ b/src/service/auth.mobile.js @@ -119,7 +119,7 @@ const removeDeviceTokenHandler = async (req, res) => { ) return res.status(401).send("unauthorized"); - await unregisterDeviceToken(accessTokenStatus.id, deviceToken); + await unregisterDeviceToken(deviceToken); res.status(200).send("success"); } catch (e) { logger.error(e); diff --git a/src/service/auth.replace.js b/src/service/auth.replace.js index 3eefeb0c..e84c843e 100644 --- a/src/service/auth.replace.js +++ b/src/service/auth.replace.js @@ -132,13 +132,10 @@ const sparcsssoHandler = (req, res) => { const logoutHandler = async (req, res) => { try { - const { id } = getLoginInfo(req); - // DB에서 deviceToken 레코드를 삭제합니다. const deviceToken = req.session?.deviceToken; if (deviceToken) { - const user = await userModel.findOne({ id }, "_id"); - await unregisterDeviceToken(user._id, deviceToken); + await unregisterDeviceToken(deviceToken); } const ssoLogoutUrl = security.frontUrl + "/login"; From 194fdf3642d6a49185bffe841dcb1f9589081b97 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 15 Mar 2023 05:15:35 +0900 Subject: [PATCH 144/308] Fix: fix getNotificationOptions handler not getting query from url --- app.js | 1 + src/modules/fcm.js | 8 +++++--- src/route/notifications.js | 4 ++-- src/service/notifications.js | 18 +++++++++++++----- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/app.js b/app.js index 18c79981..367120fd 100644 --- a/app.js +++ b/app.js @@ -59,6 +59,7 @@ app.use("/rooms", require("./src/route/rooms")); app.use("/chats", require("./src/route/chats")); app.use("/locations", require("./src/route/locations")); app.use("/reports", require("./src/route/reports")); +app.use("/notifications", require("./src/route/notifications")); // express 서버 시작 const serverHttp = http diff --git a/src/modules/fcm.js b/src/modules/fcm.js index e337c75c..f40535f2 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -15,7 +15,7 @@ const logger = require("../modules/logger"); const registerDeviceToken = async (userId, deviceToken) => { try { // 디바이스 토큰을 DB에 추가합니다. - const newDeviceToken = await deviceTokenModel.updateOne( + const newDeviceToken = await deviceTokenModel.findOneAndUpdate( { userId, }, @@ -30,9 +30,11 @@ const registerDeviceToken = async (userId, deviceToken) => { await notificationOptionModel.updateOne( { deviceToken }, { deviceToken }, - { upsert: true, new: true } + { upsert: true } ); + logger.info(newDeviceToken.deviceTokens); + return newDeviceToken.deviceTokens; } catch (error) { logger.error(error); @@ -251,7 +253,7 @@ const subscribeUserToTopic = async (userId, topic) => { // 데이터베이스에 해당 토큰에 대한 토픽 구독 레코드를 추가합니다. await Promise.all( deviceToken.deviceTokens.map(async (token) => { - return await topicSubscriptionModel.updateOne( + return await topicSubscriptionModel.findOneAndUpdate( { deviceToken: token, topic: topic, diff --git a/src/route/notifications.js b/src/route/notifications.js index 97895d08..ade4a8a9 100644 --- a/src/route/notifications.js +++ b/src/route/notifications.js @@ -1,6 +1,6 @@ const express = require("express"); const router = express.Router(); -const { body } = require("express-validator"); +const { query, body } = require("express-validator"); const notificationHandlers = require("../service/notifications"); const validator = require("../middleware/validator"); @@ -10,7 +10,7 @@ router.use(require("../middleware/auth")); router.get( "/options", - body("deviceToken").isString(), + query("deviceToken").isString(), notificationHandlers.getNotificationOptions ); diff --git a/src/service/notifications.js b/src/service/notifications.js index cad2dbc8..7f3e716b 100644 --- a/src/service/notifications.js +++ b/src/service/notifications.js @@ -3,7 +3,7 @@ const logger = require("../modules/logger"); const getNotificationOptions = async (req, res) => { try { - const { deviceToken } = req.body; + const { deviceToken } = req.query; if (!deviceToken) { return res .status(400) @@ -56,22 +56,30 @@ const changeNotificationOptions = async (req, res) => { } }); if (options.keywords) { - newOptions.keywords = options.keyword; + newOptions.keywords = options.keywords; } - - const updatedNotificationOptions = await notificationOptionModel.updateOne( + await notificationOptionModel.updateOne( { deviceToken, }, { deviceToken, - newOptions, + ...newOptions, }, { new: true, } ); + const updatedNotificationOptions = await notificationOptionModel + .findOne( + { + deviceToken, + }, + "-_id chatting keywords beforeDepart notice advertisement" + ) + .lean(); + if (!updatedNotificationOptions) res .status(400) From ca9741d4a51131b5cc58b82578dbc650a133fe25 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 15 Mar 2023 21:58:59 +0900 Subject: [PATCH 145/308] Add: deviceTokenModel and notificationOptionModel in admin.js --- src/modules/fcm.js | 2 -- src/route/admin.js | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index f40535f2..9b7ea716 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -33,8 +33,6 @@ const registerDeviceToken = async (userId, deviceToken) => { { upsert: true } ); - logger.info(newDeviceToken.deviceTokens); - return newDeviceToken.deviceTokens; } catch (error) { logger.error(error); diff --git a/src/route/admin.js b/src/route/admin.js index 188013f1..a7008b67 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -11,6 +11,8 @@ const { reportModel, adminIPWhitelistModel, adminLogModel, + deviceTokenModel, + notificationOptionModel, } = require("../db/mongo"); const router = express.Router(); @@ -84,6 +86,8 @@ const adminJS = new AdminJS({ reportModel, adminIPWhitelistModel, adminLogModel, + deviceTokenModel, + notificationOptionModel, ].map(resourceWrapper), }); router.use(AdminJSExpress.buildRouter(adminJS)); From 5c4c33128420c6ff5d640b75cf8fe4de723388a1 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Sat, 18 Mar 2023 23:52:20 +0900 Subject: [PATCH 146/308] Remove: validation --- src/service/notifications.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/service/notifications.js b/src/service/notifications.js index 7f3e716b..4aae8603 100644 --- a/src/service/notifications.js +++ b/src/service/notifications.js @@ -4,11 +4,6 @@ const logger = require("../modules/logger"); const getNotificationOptions = async (req, res) => { try { const { deviceToken } = req.query; - if (!deviceToken) { - return res - .status(400) - .send("Notification/getNotificationOptions: deviceToken not found"); - } // deviceToken에 대응되는 알림 설정을 찾아 반환합니다. const notificationOptions = await notificationOptionModel @@ -37,12 +32,6 @@ const changeNotificationOptions = async (req, res) => { try { const { deviceToken, options } = req.body; - if (!deviceToken) { - return res - .status(400) - .send("Notification/changeNotificationOptions: deviceToken not found"); - } - const newOptions = {}; const booleanFields = [ "chatting", From a17be5a353e1e4661e2637475a3306e49cfdbc9a Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Sun, 19 Mar 2023 00:00:48 +0900 Subject: [PATCH 147/308] Refactor: initializeApp --- app.js | 13 +------------ src/modules/fcm.js | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app.js b/app.js index 367120fd..9cb9811e 100644 --- a/app.js +++ b/app.js @@ -7,18 +7,7 @@ const logAPIAccess = require("./src/modules/logAPIAccess"); const startSocketServer = require("./src/modules/socket"); // Firebase Admin 초기설정 -const admin = require("firebase-admin"); -const googleApplicationCredentialsPath = security.googleApplicationCredentials; -if (googleApplicationCredentialsPath) { - const serviceAccount = require(googleApplicationCredentialsPath); - admin.initializeApp({ - credential: admin.credential.cert(serviceAccount), - }); -} else { - logger.error( - "Firebase 관련 credential이 존재하지 않습니다. FCM 관련 기능을 사용할 수 없습니다." - ); -} +require("./src/modules/fcm").initializeApp(); // 익스프레스 서버 생성 const app = express(); diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 9b7ea716..e5bff3ce 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -1,3 +1,4 @@ +const firebaseAdmin = require("firebase-admin"); const { getMessaging } = require("firebase-admin/messaging"); const { deviceTokenModel, @@ -5,6 +6,22 @@ const { topicSubscriptionModel, } = require("../db/mongo"); const logger = require("../modules/logger"); +const { googleApplicationCredentials } = require("../../security"); + +/** + * credential을 등록합니다. + */ +const initializeApp = () => { + if (googleApplicationCredentials) { + firebaseAdmin.initializeApp({ + credential: firebaseAdmin.credential.cert(require(googleApplicationCredentials)), + }); + } else { + logger.error( + "Firebase 관련 credential이 존재하지 않습니다. FCM 관련 기능을 사용할 수 없습니다." + ); + } +} /** * 사용자의 ObjectId와 FCM device token이 주어졌을 때, 해당 deviceToken을 사용자의 토큰으로 DB에 등록합니다. @@ -320,6 +337,7 @@ const unsubscribeUserFromTopic = async (userId, topic) => { }; module.exports = { + initializeApp, registerDeviceToken, unregisterDeviceToken, validateDeviceToken, From aa2d7418235ea530cfb9d8b65bb380a101445fb1 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Mon, 20 Mar 2023 20:15:36 +0900 Subject: [PATCH 148/308] Fix: fcm.js to hard env --- src/modules/fcm.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index e5bff3ce..76257272 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -14,14 +14,16 @@ const { googleApplicationCredentials } = require("../../security"); const initializeApp = () => { if (googleApplicationCredentials) { firebaseAdmin.initializeApp({ - credential: firebaseAdmin.credential.cert(require(googleApplicationCredentials)), + credential: firebaseAdmin.credential.cert( + require("../../firebase-admin-sdk-account.json") + ), }); } else { logger.error( "Firebase 관련 credential이 존재하지 않습니다. FCM 관련 기능을 사용할 수 없습니다." ); } -} +}; /** * 사용자의 ObjectId와 FCM device token이 주어졌을 때, 해당 deviceToken을 사용자의 토큰으로 DB에 등록합니다. From 6e283b76d1499c9266e8e7e64564b73d24f4148b Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 21 Mar 2023 21:55:01 +0900 Subject: [PATCH 149/308] Remove: "/admin/logs" page --- app.js | 1 - src/route/admin.logs.js | 12 ------------ 2 files changed, 13 deletions(-) delete mode 100644 src/route/admin.logs.js diff --git a/app.js b/app.js index cce0a333..aaa3bc8d 100644 --- a/app.js +++ b/app.js @@ -28,7 +28,6 @@ app.use(require("./src/middleware/information")); app.use(require("response-time")(logAPIAccess)); // [Router] admin 페이지는 rate limiting을 적용하지 않습니다. -app.use("/admin/logs", require("./src/route/admin.logs")); app.use("/admin", require("./src/route/admin")); // [Middleware] 모든 요청에 대하여 rate limiting 적용 diff --git a/src/route/admin.logs.js b/src/route/admin.logs.js deleted file mode 100644 index 619ab3f8..00000000 --- a/src/route/admin.logs.js +++ /dev/null @@ -1,12 +0,0 @@ -const express = require("express"); -const path = require("path"); - -const router = express.Router(); - -// Requires admin property of the user to enter admin page. -router.use(require("../middleware/authAdmin")); - -// Log 파일 제공 -router.use(express.static(path.join(process.cwd(), "logs"))); - -module.exports = router; From 59223a5b99953fca57e4d96ae85060d176d39dc7 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 21 Mar 2023 21:56:34 +0900 Subject: [PATCH 150/308] Remove: auth middleware on locations api --- src/route/locations.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/route/locations.js b/src/route/locations.js index 9e56d29b..3e08bb97 100644 --- a/src/route/locations.js +++ b/src/route/locations.js @@ -3,9 +3,6 @@ const express = require("express"); const router = express.Router(); const locationsHandlers = require("../service/locations"); -// 라우터 접근 시 로그인 필요 -router.use(require("../middleware/auth")); - /** * @swagger * /locations: From 8e11f25e27a02b209dae82f90f297ebe03c99ae4 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 21 Mar 2023 13:03:24 +0000 Subject: [PATCH 151/308] Docs: integrate swagger docs --- src/route/docs.js | 30 +--- src/route/docs/docs.json | 323 +++++++++++++++++++++++++++++++++++++++ src/route/locations.js | 41 ----- src/route/logininfo.js | 85 ----------- src/route/reports.js | 84 ---------- 5 files changed, 324 insertions(+), 239 deletions(-) create mode 100644 src/route/docs/docs.json diff --git a/src/route/docs.js b/src/route/docs.js index 44c716d4..da7ebfa2 100644 --- a/src/route/docs.js +++ b/src/route/docs.js @@ -1,35 +1,7 @@ const express = require("express"); const swaggerUi = require("swagger-ui-express"); -const swaggereJsdoc = require("swagger-jsdoc"); - +const swaggerSpec = require("../route/docs/docs.json"); const router = express.Router(); -const swaggerSpec = swaggereJsdoc({ - definition: { - openapi: "3.0.3", - info: { - title: "Taxi API Document", - version: "1.0.0", - }, - basePath: "/", - tags: [ - { - name: "locations", - description: "출발지/도착지 정보 제공", - }, - { - name: "logininfo", - description: "로그인 정보 제공", - }, - { - name: "reports", - description: "사용자 신고 및 신고 기록 조회", - }, - ], - consumes: ["application/json"], - produces: ["application/json"], - }, - apis: ["src/route/*.js"], -}); router.use(swaggerUi.serve); router.use(swaggerUi.setup(swaggerSpec, { explorer: true })); diff --git a/src/route/docs/docs.json b/src/route/docs/docs.json new file mode 100644 index 00000000..a2b4bfb8 --- /dev/null +++ b/src/route/docs/docs.json @@ -0,0 +1,323 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Taxi API Document", + "version": "1.0.0" + }, + "basePath": "/", + "tags": [ + { + "name": "locations", + "description": "출발지/도착지 정보 제공" + }, + { + "name": "logininfo", + "description": "로그인 정보 제공" + }, + { + "name": "reports", + "description": "사용자 신고 및 신고 기록 조회" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/create": { + "post": { + "tags": [ + "reports" + ], + "summary": "신고 작성", + "description": "주어진 유저를 전달된 사유로 신고함", + "requestBody": { + "description": "Update an existent user in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createHandler" + } + } + } + }, + "responses": { + "200": { + "description": "report successful", + "content": { + "text/plain": { + "schema": { + "type": "string", + "example": "report successful" + } + } + } + }, + "500": { + "description": "internal server error", + "content": { + "text/plain": { + "schema": { + "type": "string", + "example": "internal server error" + } + } + } + } + } + } + }, + "/searchByUser": { + "get": { + "tags": [ + "reports" + ], + "summary": "신고 내역 반환", + "description": "로그인된 사용자의 신고한 내역과, 신고받은 내역을 반환한다
1000개의 limit이 있다.", + "responses": { + "200": { + "description": "신고된 내역과 신고 받은 내역", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "reporting": { + "type": "array" + }, + "reported": { + "type": "array" + } + } + } + } + } + }, + "500": { + "description": "internal server error", + "content": { + "text/plain": { + "schema": { + "type": "string", + "example": "internal server error" + } + } + } + } + } + } + }, + "/logininfo/detail": { + "get": { + "tags": [ + "logininfo" + ], + "summary": "상세한 사용자 정보 반환", + "description": "로그인되어 있는 사용자의 상세한 정보를 반환", + "responses": { + "200": { + "description": "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
\n 세션이 유효하지 않은 경우, 빈 오브젝트를 반환", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "oid": { + "type": "string" + }, + "id": { + "type": "string", + "description": "사용자 id" + }, + "name": { + "type": "string", + "description": "사용자 이름" + }, + "nickname": { + "type": "string" + }, + "withdraw": { + "type": "boolean" + }, + "ban": { + "type": "boolean" + }, + "joinat": { + "type": "string", + "format": "date-time" + }, + "agreeOnTermsOfService": { + "type": "boolean" + }, + "subinfio": { + "type": "object", + "properties": { + "kaist": { + "type": "string", + "description": "KAIST 학번(8자리)", + "minLength": 8, + "maxLength": 8, + "example": "20190052" + }, + "sparcs": { + "type": "string" + }, + "facebook": { + "type": "string" + }, + "twitter": { + "type": "string" + } + } + }, + "email": { + "type": "string", + "example": "geon6757@kaist.ac.kr" + }, + "profileImgUrl": { + "type": "string" + }, + "account": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "/logininfo": { + "get": { + "tags": [ + "logininfo" + ], + "summary": "사용자 정보 반환", + "description": "로그인되어 있는 사용자의 정보를 반환", + "responses": { + "200": { + "description": "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
세션이 유효하지 않은 경우, 빈 오브젝트를 반환", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "사용자 id" + }, + "sid": { + "type": "string", + "description": "사용자 sid" + }, + "name": { + "type": "string", + "description": "사용자 이름" + } + } + } + } + } + } + } + } + }, + "/locations": { + "get": { + "tags": [ + "locations" + ], + "summary": "출발지/도착지 정보 반환", + "description": "출발지/도착지로 사용 가능한 장소 목록 조회 및 요청 처리 당시 서버 시각 반환
\n (로그인된 상태에서만 접근 가능)", + "responses": { + "200": { + "description": "서버에 저장된 location이 없을 경우, locations은 빈 배열", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "locations": { + "type": "array", + "description": "출발지/도착지로 사용 가능한 장소 목록", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "number" + }, + "isValid": { + "type": "boolean" + }, + "_id": { + "type": "string" + }, + "koName": { + "type": "string", + "description": "장소의 한국어 명칭", + "example": "택시승강장" + }, + "enName": { + "type": "string", + "description": "장소의 영어 명칭", + "example": "Taxi Stand" + } + } + } + }, + "serverTime": { + "type": "string", + "format": "date-time", + "description": "요청 처리 당시 서버 시각" + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "createHandler": { + "type": "object", + "required": [ + "reportedId", + "type", + "time" + ], + "properties": { + "reportedId": { + "type": "string", + "pattern": "^[a-fA-F\\d]{24}$" + }, + "type": { + "type": "string", + "enum": [ + "no-settlement", + "no-show", + "etc-reason" + ] + }, + "etcDetail": { + "type": "string", + "maxLength": 30 + }, + "time": { + "type": "string", + "format": "date-time" + } + } + } + } + }, + "apis": [ + "src/route/*.js", + "src/route/docs/*.json" + ] +} \ No newline at end of file diff --git a/src/route/locations.js b/src/route/locations.js index 9e56d29b..4cdd34c9 100644 --- a/src/route/locations.js +++ b/src/route/locations.js @@ -6,47 +6,6 @@ const locationsHandlers = require("../service/locations"); // 라우터 접근 시 로그인 필요 router.use(require("../middleware/auth")); -/** - * @swagger - * /locations: - * get: - * tags: [locations] - * summary: 출발지/도착지 정보 반환 - * description: 출발지/도착지로 사용 가능한 장소 목록 조회 및 요청 처리 당시 서버 시각 반환
- * (로그인된 상태에서만 접근 가능) - * responses: - * 200: - * description: 서버에 저장된 location이 없을 경우, locations은 빈 배열 - * content: - * application/json: - * schema: - * type: object - * properties: - * locations: - * type: array - * description: 출발지/도착지로 사용 가능한 장소 목록 - * items: - * type: object - * properties: - * priority: - * type: number - * isValid: - * type: boolean - * _id: - * type: string - * koName: - * type: string - * description: 장소의 한국어 명칭 - * example: 택시승강장 - * enName: - * type: string - * description: 장소의 영어 명칭 - * example: Taxi Stand - * serverTime: - * type: string - * format: date-time - * description: 요청 처리 당시 서버 시각 - */ router.get("/", locationsHandlers.getAllLocationsHandler); module.exports = router; diff --git a/src/route/logininfo.js b/src/route/logininfo.js index d221dd8c..6e746682 100644 --- a/src/route/logininfo.js +++ b/src/route/logininfo.js @@ -3,92 +3,7 @@ const express = require("express"); const router = express.Router(); const logininfoHandlers = require("../service/logininfo"); -/** - * @swagger - * /logininfo: - * get: - * tags: [logininfo] - * summary: 사용자 정보 반환 - * description: 로그인되어 있는 사용자의 정보를 반환 - * responses: - * 200: - * description: 사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
- * 세션이 유효하지 않은 경우, 빈 오브젝트를 반환 - * content: - * application/json: - * schema: - * type: object - * properties: - * id: - * type: string - * description: 사용자 id - * sid: - * type: string - * description: 사용자 sid - * name: - * type: string - * description: 사용자 이름 - */ router.route("/").get(logininfoHandlers.logininfoHandler); - -/** - * @swagger - * /logininfo/detail: - * get: - * tags: [logininfo] - * summary: 상세한 사용자 정보 반환 - * description: 로그인되어 있는 사용자의 상세한 정보를 반환 - * responses: - * 200: - * description: 사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
- * 세션이 유효하지 않은 경우, 빈 오브젝트를 반환 - * content: - * application/json: - * schema: - * type: object - * properties: - * oid: - * type: string - * id: - * type: string - * description: 사용자 id - * name: - * type: string - * description: 사용자 이름 - * nickname: - * type: string - * withdraw: - * type: boolean - * ban: - * type: boolean - * joinat: - * type: string - * format: date-time - * agreeOnTermsOfService: - * type: boolean - * subinfio: - * type: object - * properties: - * kaist: - * type: string - * description: KAIST 학번(8자리) - * minLength: 8 - * maxLength: 8 - * example: 20190052 - * sparcs: - * type: string - * facebook: - * type: string - * twitter: - * type: string - * email: - * type: string - * example: geon6757@kaist.ac.kr - * profileImgUrl: - * type: string - * account: - * type: string - */ router.route("/detail").get(logininfoHandlers.detailHandler); module.exports = router; diff --git a/src/route/reports.js b/src/route/reports.js index 9369c273..16525f31 100644 --- a/src/route/reports.js +++ b/src/route/reports.js @@ -7,56 +7,6 @@ const reportHandlers = require("../service/reports"); // 라우터 접근 시 로그인 필요 router.use(require("../middleware/auth")); -/** - * @swagger - * /create: - * post: - * tags: [reports] - * summary: 신고 작성 - * description: 주어진 유저를 전달된 사유로 신고함 - * requestBody: - * description: Update an existent user in the store - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/createHandler' - * responses: - * 200: - * description: report successful - * content: - * text/plain: - * schema: - * type: string - * example: report successful - * 500: - * description: internal server error - * content: - * text/plain: - * schema: - * type: string - * example: internal server error - * components: - * schemas: - * createHandler: - * type: object - * required: - * - reportedId - * - type - * - time - * properties: - * reportedId: - * type: string - * pattern: '^[a-fA-F\d]{24}$' - * type: - * type: string - * enum: ["no-settlement", "no-show", "etc-reason"] - * etcDetail: - * type: string - * maxLength: 30 - * time: - * type: string - * format: date-time - */ router.post( "/create", [ @@ -69,40 +19,6 @@ router.post( reportHandlers.createHandler ); -/** - * @swagger - * /searchByUser: - * get: - * tags: [reports] - * summary: 신고 내역 반환 - * description: 로그인된 사용자의 신고한 내역과, 신고받은 내역을 반환한다
- * 1000개의 limit이 있다. - * responses: - * 200: - * description: 신고된 내역과 신고 받은 내역 - * content: - * application/json: - * schema: - * type: object - * properties: - * reporting: - * type: array - * items: - * type: object - * description: Report - * reported: - * type: array - * items: - * type: object - * description: Report - * 500: - * description: "internal server error" - * content: - * text/plain: - * schema: - * type: string - * example: "internal server error" - */ router.get("/searchByUser", reportHandlers.searchByUserHandler); module.exports = router; From 232be498fef38b92369e6520f8f3aad2e4756dbd Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 21 Mar 2023 22:28:15 +0900 Subject: [PATCH 152/308] Add: add icon, click_action property to data payload --- src/modules/fcm.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 76257272..8128b37b 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -180,6 +180,11 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { try { const message = { tokens, + data: { + url: link || "/", + icon: icon || "/icons-512.png", + click_action: "FLUTTER_NOTIFICATION_CLICK", + }, notification: { title, body, From 62434d011ffa920c2a901457e720b0ab1703a271 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 21 Mar 2023 22:59:02 +0900 Subject: [PATCH 153/308] Refactor: googleApplicationCredentials env --- .env.example | 2 +- .gitignore | 3 --- security.js | 8 ++++++-- src/modules/fcm.js | 4 +--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index 7c5dd279..303ffb31 100644 --- a/.env.example +++ b/.env.example @@ -11,4 +11,4 @@ AWS_S3_BUCKET_NAME=[AWS S3 Buck name] AWS_S3_URL=[AWS S3 url(e.g. https://.s3..amazonaws.com)] JWT_SECRET_KEY=[JWT SERCRET KEY] APP_URI_SCHEME=[APP_URI_SCHEME] -GOOGLE_APPLICATION_CREDENTIALS=firebase-admin-sdk-account.json +GOOGLE_APPLICATION_CREDENTIALS=[GOOGLE APPLICATION CREDENTIALS JSON] diff --git a/.gitignore b/.gitignore index 6f7fc250..8ffa241b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,3 @@ # AdminJS 관련 디렉토리 .adminjs - -# Firebase Admin SDK 크레덴셜 -firebase-admin-sdk-account.json diff --git a/security.js b/security.js index 3efe3fc2..9b947f16 100644 --- a/security.js +++ b/security.js @@ -16,9 +16,13 @@ module.exports = { accessKeyId: process.env.AWS_ACCESS_KEY_ID, // required secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, // required s3BucketName: process.env.AWS_S3_BUCKET_NAME, // required - s3Url: process.env.AWS_S3_URL || "https://${process.env.AWS_S3_BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com", // optional + s3Url: + process.env.AWS_S3_URL || + "https://${process.env.AWS_S3_BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com", // optional }, jwtSecretKey: process.env.JWT_SECRET_KEY, appUriScheme: process.env.APP_URI_SCHEME, - googleApplicationCredentials: process.env.GOOGLE_APPLICATION_CREDENTIALS, + googleApplicationCredentials: + process.env.GOOGLE_APPLICATION_CREDENTIALS && + JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS), }; diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 8128b37b..e569392b 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -14,9 +14,7 @@ const { googleApplicationCredentials } = require("../../security"); const initializeApp = () => { if (googleApplicationCredentials) { firebaseAdmin.initializeApp({ - credential: firebaseAdmin.credential.cert( - require("../../firebase-admin-sdk-account.json") - ), + credential: firebaseAdmin.credential.cert(googleApplicationCredentials), }); } else { logger.error( From e7624eb3f2029cdd01efaee24edafe0e77d6f885 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 21 Mar 2023 23:43:12 +0900 Subject: [PATCH 154/308] Rename: changeNotificationOptionsHandler and getNotificationOptionsHandler --- src/route/notifications.js | 4 ++-- src/service/notifications.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/route/notifications.js b/src/route/notifications.js index ade4a8a9..a57fffe4 100644 --- a/src/route/notifications.js +++ b/src/route/notifications.js @@ -11,7 +11,7 @@ router.use(require("../middleware/auth")); router.get( "/options", query("deviceToken").isString(), - notificationHandlers.getNotificationOptions + notificationHandlers.getNotificationOptionsHandler ); router.patch( @@ -25,7 +25,7 @@ router.patch( body("options.notice").optional().isBoolean(), body("options.advertisement").optional().isBoolean(), validator, - notificationHandlers.changeNotificationOptions + notificationHandlers.changeNotificationOptionsHandler ); module.exports = router; diff --git a/src/service/notifications.js b/src/service/notifications.js index 4aae8603..9341a829 100644 --- a/src/service/notifications.js +++ b/src/service/notifications.js @@ -1,7 +1,7 @@ const { notificationOptionModel } = require("../db/mongo"); const logger = require("../modules/logger"); -const getNotificationOptions = async (req, res) => { +const getNotificationOptionsHandler = async (req, res) => { try { const { deviceToken } = req.query; @@ -28,7 +28,7 @@ const getNotificationOptions = async (req, res) => { } }; -const changeNotificationOptions = async (req, res) => { +const changeNotificationOptionsHandler = async (req, res) => { try { const { deviceToken, options } = req.body; @@ -84,6 +84,6 @@ const changeNotificationOptions = async (req, res) => { }; module.exports = { - getNotificationOptions, - changeNotificationOptions, + getNotificationOptionsHandler, + changeNotificationOptionsHandler, }; From d2059678219b5c06b111e20468c4016605932ce8 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 00:11:46 +0900 Subject: [PATCH 155/308] Fix: notifications.js --- src/service/notifications.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/service/notifications.js b/src/service/notifications.js index 9341a829..bc568f3a 100644 --- a/src/service/notifications.js +++ b/src/service/notifications.js @@ -1,7 +1,7 @@ const { notificationOptionModel } = require("../db/mongo"); const logger = require("../modules/logger"); -const getNotificationOptionsHandler = async (req, res) => { +const optionsHandler = async (req, res) => { try { const { deviceToken } = req.query; @@ -28,10 +28,11 @@ const getNotificationOptionsHandler = async (req, res) => { } }; -const changeNotificationOptionsHandler = async (req, res) => { +const editOptionsHandler = async (req, res) => { try { const { deviceToken, options } = req.body; + // FIXME : can refactor with using reduce const newOptions = {}; const booleanFields = [ "chatting", @@ -47,6 +48,7 @@ const changeNotificationOptionsHandler = async (req, res) => { if (options.keywords) { newOptions.keywords = options.keywords; } + await notificationOptionModel.updateOne( { deviceToken, @@ -84,6 +86,6 @@ const changeNotificationOptionsHandler = async (req, res) => { }; module.exports = { - getNotificationOptionsHandler, - changeNotificationOptionsHandler, + optionsHandler, + editOptionsHandler, }; From 962c064b08a8ac2e525ccd2b27bc2d45482e67c1 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 00:26:48 +0900 Subject: [PATCH 156/308] Fix: notifications.js --- src/route/notifications.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/route/notifications.js b/src/route/notifications.js index a57fffe4..3954f30e 100644 --- a/src/route/notifications.js +++ b/src/route/notifications.js @@ -11,11 +11,11 @@ router.use(require("../middleware/auth")); router.get( "/options", query("deviceToken").isString(), - notificationHandlers.getNotificationOptionsHandler + notificationHandlers.optionsHandler ); -router.patch( - "/options", +router.post( + "/editOptions", body("deviceToken").isString(), body("options").isObject(), body("options.chatting").optional().isBoolean(), @@ -25,7 +25,7 @@ router.patch( body("options.notice").optional().isBoolean(), body("options.advertisement").optional().isBoolean(), validator, - notificationHandlers.changeNotificationOptionsHandler + notificationHandlers.editOptionsHandler ); module.exports = router; From 326273013b94152a1eca565cfc8271eee7a42eff Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 00:37:12 +0900 Subject: [PATCH 157/308] Rename: middlewares --- app.js | 6 +++--- src/{middleware => middlewares}/auth.js | 0 src/{middleware => middlewares}/authAdmin.js | 0 src/{middleware => middlewares}/information.js | 0 src/{middleware => middlewares}/limitRate.js | 0 src/{middleware => middlewares}/session.js | 0 src/{middleware => middlewares}/validator.js | 0 src/route/admin.js | 4 ++-- src/route/auth.js | 4 ++-- src/route/auth.replace.js | 4 ++-- src/route/chats.js | 4 ++-- src/route/notifications.js | 4 ++-- src/route/reports.js | 4 ++-- src/route/rooms.js | 4 ++-- src/route/users.js | 4 ++-- 15 files changed, 19 insertions(+), 19 deletions(-) rename src/{middleware => middlewares}/auth.js (100%) rename src/{middleware => middlewares}/authAdmin.js (100%) rename src/{middleware => middlewares}/information.js (100%) rename src/{middleware => middlewares}/limitRate.js (100%) rename src/{middleware => middlewares}/session.js (100%) rename src/{middleware => middlewares}/validator.js (100%) diff --git a/app.js b/app.js index 258dbce2..df3b6c51 100644 --- a/app.js +++ b/app.js @@ -20,12 +20,12 @@ app.use(express.json()); app.use(require("cors")({ origin: true, credentials: true })); // [Middleware] 세션 및 쿠키 -const session = require("./src/middleware/session"); +const session = require("./src/middlewares/session"); app.use(session); app.use(require("cookie-parser")()); // [Middleware] Timestamp 및 clientIP 확인 -app.use(require("./src/middleware/information")); +app.use(require("./src/middlewares/information")); // [Middleware] API 접근 기록 및 응답 시간을 http response의 헤더에 기록합니다. app.use(require("response-time")(logAPIAccess)); @@ -34,7 +34,7 @@ app.use(require("response-time")(logAPIAccess)); app.use("/admin", require("./src/route/admin")); // [Middleware] 모든 요청에 대하여 rate limiting 적용 -app.use(require("./src/middleware/limitRate")); +app.use(require("./src/middlewares/limitRate")); // [Router] Swagger (API 문서) app.use("/docs", require("./src/route/docs")); diff --git a/src/middleware/auth.js b/src/middlewares/auth.js similarity index 100% rename from src/middleware/auth.js rename to src/middlewares/auth.js diff --git a/src/middleware/authAdmin.js b/src/middlewares/authAdmin.js similarity index 100% rename from src/middleware/authAdmin.js rename to src/middlewares/authAdmin.js diff --git a/src/middleware/information.js b/src/middlewares/information.js similarity index 100% rename from src/middleware/information.js rename to src/middlewares/information.js diff --git a/src/middleware/limitRate.js b/src/middlewares/limitRate.js similarity index 100% rename from src/middleware/limitRate.js rename to src/middlewares/limitRate.js diff --git a/src/middleware/session.js b/src/middlewares/session.js similarity index 100% rename from src/middleware/session.js rename to src/middlewares/session.js diff --git a/src/middleware/validator.js b/src/middlewares/validator.js similarity index 100% rename from src/middleware/validator.js rename to src/middlewares/validator.js diff --git a/src/route/admin.js b/src/route/admin.js index a7008b67..4d5cf65a 100644 --- a/src/route/admin.js +++ b/src/route/admin.js @@ -18,8 +18,8 @@ const { const router = express.Router(); // Requires admin property of the user to enter admin page. -router.use(require("../middleware/authAdmin")); -router.use(require("../middleware/auth")); +router.use(require("../middlewares/authAdmin")); +router.use(require("../middlewares/auth")); // Registration of the mongoose adapter AdminJS.registerAdapter(AdminJSMongoose); diff --git a/src/route/auth.js b/src/route/auth.js index 9c978630..c3f7e912 100644 --- a/src/route/auth.js +++ b/src/route/auth.js @@ -2,8 +2,8 @@ const express = require("express"); const router = express.Router(); const { body } = require("express-validator"); -const authMiddleware = require("../middleware/auth"); -const validator = require("../middleware/validator"); +const authMiddleware = require("../middlewares/auth"); +const validator = require("../middlewares/validator"); const authHandlers = require("../service/auth"); const mobileAuthHandlers = require("../service/auth.mobile"); diff --git a/src/route/auth.replace.js b/src/route/auth.replace.js index 34380d17..3ca5f98c 100755 --- a/src/route/auth.replace.js +++ b/src/route/auth.replace.js @@ -3,8 +3,8 @@ const router = express.Router(); const { body } = require("express-validator"); const authReplaceHandlers = require("../service/auth.replace"); -const authMiddleware = require("../middleware/auth"); -const validator = require("../middleware/validator"); +const authMiddleware = require("../middlewares/auth"); +const validator = require("../middlewares/validator"); // 로그인 시도 router.route("/try").post(authReplaceHandlers.tryHandler); diff --git a/src/route/chats.js b/src/route/chats.js index abec55cd..315f0dc1 100644 --- a/src/route/chats.js +++ b/src/route/chats.js @@ -1,13 +1,13 @@ const express = require("express"); const { body } = require("express-validator"); -const validator = require("../middleware/validator"); +const validator = require("../middlewares/validator"); const patterns = require("../db/patterns"); const router = express.Router(); const chatsHandlers = require("../service/chats"); // 라우터 접근 시 로그인 필요 -router.use(require("../middleware/auth")); +router.use(require("../middlewares/auth")); // 채팅 이미지를 업로드할 수 있는 Presigned-url을 발급합니다. router.post( diff --git a/src/route/notifications.js b/src/route/notifications.js index 3954f30e..044004b1 100644 --- a/src/route/notifications.js +++ b/src/route/notifications.js @@ -3,10 +3,10 @@ const router = express.Router(); const { query, body } = require("express-validator"); const notificationHandlers = require("../service/notifications"); -const validator = require("../middleware/validator"); +const validator = require("../middlewares/validator"); // 라우터 접근 시 로그인 필요 -router.use(require("../middleware/auth")); +router.use(require("../middlewares/auth")); router.get( "/options", diff --git a/src/route/reports.js b/src/route/reports.js index e55632e7..19cc4ed5 100644 --- a/src/route/reports.js +++ b/src/route/reports.js @@ -1,12 +1,12 @@ const express = require("express"); const { body } = require("express-validator"); -const validator = require("../middleware/validator"); +const validator = require("../middlewares/validator"); const router = express.Router(); const reportHandlers = require("../service/reports"); // 라우터 접근 시 로그인 필요 -router.use(require("../middleware/auth")); +router.use(require("../middlewares/auth")); router.post( "/create", diff --git a/src/route/rooms.js b/src/route/rooms.js index c463efe8..8750643d 100644 --- a/src/route/rooms.js +++ b/src/route/rooms.js @@ -3,11 +3,11 @@ const { query, body } = require("express-validator"); const router = express.Router(); const roomHandlers = require("../service/rooms"); -const validator = require("../middleware/validator"); +const validator = require("../middlewares/validator"); const patterns = require("../db/patterns"); // 라우터 접근 시 로그인 필요 -router.use(require("../middleware/auth")); +router.use(require("../middlewares/auth")); // 특정 id 방 세부사항 보기 router.get( diff --git a/src/route/users.js b/src/route/users.js index 06f519de..07975b4a 100755 --- a/src/route/users.js +++ b/src/route/users.js @@ -1,6 +1,6 @@ const express = require("express"); const { body } = require("express-validator"); -const validator = require("../middleware/validator"); +const validator = require("../middlewares/validator"); const patterns = require("../db/patterns"); const router = express.Router(); @@ -9,7 +9,7 @@ const userHandlers = require("../service/users"); const { replaceSpaceInNickname } = require("../modules/modifyProfile"); // 라우터 접근 시 로그인 필요 -router.use(require("../middleware/auth")); +router.use(require("../middlewares/auth")); // 이용 약관에 동의합니다. router.post( From 9dbd5968cd628940bd297e5303a03c67bb8f3ba9 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 00:39:27 +0900 Subject: [PATCH 158/308] Rename: routes --- README.md | 2 +- app.js | 20 ++++++++++---------- src/modules/socket.js | 2 +- src/{route => routes}/admin.js | 0 src/{route => routes}/auth.js | 0 src/{route => routes}/auth.replace.js | 0 src/{route => routes}/chats.js | 0 src/{route => routes}/chats.socket.js | 0 src/{route => routes}/docs.js | 2 +- src/{route => routes}/docs/README.md | 0 src/{route => routes}/docs/auth.md | 0 src/{route => routes}/docs/auth.replace.md | 0 src/{route => routes}/docs/chats.md | 0 src/{route => routes}/docs/reports.md | 0 src/{route => routes}/docs/rooms.md | 0 src/{route => routes}/docs/users.md | 0 src/{route => routes}/locations.js | 0 src/{route => routes}/logininfo.js | 0 src/{route => routes}/notifications.js | 0 src/{route => routes}/reports.js | 0 src/{route => routes}/rooms.js | 0 src/{route => routes}/users.js | 0 src/service/chats.js | 2 +- src/service/rooms.js | 2 +- 24 files changed, 15 insertions(+), 15 deletions(-) rename src/{route => routes}/admin.js (100%) rename src/{route => routes}/auth.js (100%) rename src/{route => routes}/auth.replace.js (100%) rename src/{route => routes}/chats.js (100%) rename src/{route => routes}/chats.socket.js (100%) rename src/{route => routes}/docs.js (92%) rename src/{route => routes}/docs/README.md (100%) rename src/{route => routes}/docs/auth.md (100%) rename src/{route => routes}/docs/auth.replace.md (100%) rename src/{route => routes}/docs/chats.md (100%) rename src/{route => routes}/docs/reports.md (100%) rename src/{route => routes}/docs/rooms.md (100%) rename src/{route => routes}/docs/users.md (100%) rename src/{route => routes}/locations.js (100%) rename src/{route => routes}/logininfo.js (100%) rename src/{route => routes}/notifications.js (100%) rename src/{route => routes}/reports.js (100%) rename src/{route => routes}/rooms.js (100%) rename src/{route => routes}/users.js (100%) diff --git a/README.md b/README.md index de60197f..0af3da05 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ See [notion page](https://www.notion.so/sparcs/Environment-Variables-1b404bd385f Refer to [.env.example](.env.example) and write your own `.env.development` and `.env.test`. ## Backend Route Information -See [Backend Route Documentation](src/route/docs/README.md) +See [Backend Route Documentation](src/routes/docs/README.md) ## License This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details diff --git a/app.js b/app.js index df3b6c51..fb3274c5 100644 --- a/app.js +++ b/app.js @@ -31,23 +31,23 @@ app.use(require("./src/middlewares/information")); app.use(require("response-time")(logAPIAccess)); // [Router] admin 페이지는 rate limiting을 적용하지 않습니다. -app.use("/admin", require("./src/route/admin")); +app.use("/admin", require("./src/routes/admin")); // [Middleware] 모든 요청에 대하여 rate limiting 적용 app.use(require("./src/middlewares/limitRate")); // [Router] Swagger (API 문서) -app.use("/docs", require("./src/route/docs")); +app.use("/docs", require("./src/routes/docs")); // [Router] APIs -app.use("/auth", require("./src/route/auth")); -app.use("/logininfo", require("./src/route/logininfo")); -app.use("/users", require("./src/route/users")); -app.use("/rooms", require("./src/route/rooms")); -app.use("/chats", require("./src/route/chats")); -app.use("/locations", require("./src/route/locations")); -app.use("/reports", require("./src/route/reports")); -app.use("/notifications", require("./src/route/notifications")); +app.use("/auth", require("./src/routes/auth")); +app.use("/logininfo", require("./src/routes/logininfo")); +app.use("/users", require("./src/routes/users")); +app.use("/rooms", require("./src/routes/rooms")); +app.use("/chats", require("./src/routes/chats")); +app.use("/locations", require("./src/routes/locations")); +app.use("/reports", require("./src/routes/reports")); +app.use("/notifications", require("./src/routes/notifications")); // express 서버 시작 const serverHttp = http diff --git a/src/modules/socket.js b/src/modules/socket.js index 4381ba5c..71a4f992 100644 --- a/src/modules/socket.js +++ b/src/modules/socket.js @@ -2,7 +2,7 @@ const { Server } = require("socket.io"); const sharedsession = require("express-socket.io-session"); const cookieParser = require("cookie-parser"); const security = require("../../security"); -const { ioListeners } = require("../route/chats.socket"); +const { ioListeners } = require("../routes/chats.socket"); // server: express server // session: session middleware diff --git a/src/route/admin.js b/src/routes/admin.js similarity index 100% rename from src/route/admin.js rename to src/routes/admin.js diff --git a/src/route/auth.js b/src/routes/auth.js similarity index 100% rename from src/route/auth.js rename to src/routes/auth.js diff --git a/src/route/auth.replace.js b/src/routes/auth.replace.js similarity index 100% rename from src/route/auth.replace.js rename to src/routes/auth.replace.js diff --git a/src/route/chats.js b/src/routes/chats.js similarity index 100% rename from src/route/chats.js rename to src/routes/chats.js diff --git a/src/route/chats.socket.js b/src/routes/chats.socket.js similarity index 100% rename from src/route/chats.socket.js rename to src/routes/chats.socket.js diff --git a/src/route/docs.js b/src/routes/docs.js similarity index 92% rename from src/route/docs.js rename to src/routes/docs.js index 3e7ccb51..5d89353d 100644 --- a/src/route/docs.js +++ b/src/routes/docs.js @@ -24,7 +24,7 @@ const swaggerSpec = swaggereJsdoc({ consumes: ["application/json"], produces: ["application/json"], }, - apis: ["src/route/*.js"], + apis: ["src/routes/*.js"], }); router.use(swaggerUi.serve); diff --git a/src/route/docs/README.md b/src/routes/docs/README.md similarity index 100% rename from src/route/docs/README.md rename to src/routes/docs/README.md diff --git a/src/route/docs/auth.md b/src/routes/docs/auth.md similarity index 100% rename from src/route/docs/auth.md rename to src/routes/docs/auth.md diff --git a/src/route/docs/auth.replace.md b/src/routes/docs/auth.replace.md similarity index 100% rename from src/route/docs/auth.replace.md rename to src/routes/docs/auth.replace.md diff --git a/src/route/docs/chats.md b/src/routes/docs/chats.md similarity index 100% rename from src/route/docs/chats.md rename to src/routes/docs/chats.md diff --git a/src/route/docs/reports.md b/src/routes/docs/reports.md similarity index 100% rename from src/route/docs/reports.md rename to src/routes/docs/reports.md diff --git a/src/route/docs/rooms.md b/src/routes/docs/rooms.md similarity index 100% rename from src/route/docs/rooms.md rename to src/routes/docs/rooms.md diff --git a/src/route/docs/users.md b/src/routes/docs/users.md similarity index 100% rename from src/route/docs/users.md rename to src/routes/docs/users.md diff --git a/src/route/locations.js b/src/routes/locations.js similarity index 100% rename from src/route/locations.js rename to src/routes/locations.js diff --git a/src/route/logininfo.js b/src/routes/logininfo.js similarity index 100% rename from src/route/logininfo.js rename to src/routes/logininfo.js diff --git a/src/route/notifications.js b/src/routes/notifications.js similarity index 100% rename from src/route/notifications.js rename to src/routes/notifications.js diff --git a/src/route/reports.js b/src/routes/reports.js similarity index 100% rename from src/route/reports.js rename to src/routes/reports.js diff --git a/src/route/rooms.js b/src/routes/rooms.js similarity index 100% rename from src/route/rooms.js rename to src/routes/rooms.js diff --git a/src/route/users.js b/src/routes/users.js similarity index 100% rename from src/route/users.js rename to src/routes/users.js diff --git a/src/service/chats.js b/src/service/chats.js index d349cac7..d938214b 100644 --- a/src/service/chats.js +++ b/src/service/chats.js @@ -1,7 +1,7 @@ const { chatModel, userModel } = require("../db/mongo"); const awsS3 = require("../db/awsS3"); -const { emitChatEvent } = require("../route/chats.socket"); +const { emitChatEvent } = require("../routes/chats.socket"); const uploadChatImgGetPUrlHandler = async (req, res) => { try { diff --git a/src/service/rooms.js b/src/service/rooms.js index 3a1ecd98..a8b93730 100644 --- a/src/service/rooms.js +++ b/src/service/rooms.js @@ -1,5 +1,5 @@ const { roomModel, locationModel, userModel } = require("../db/mongo"); -const { emitChatEvent } = require("../route/chats.socket"); +const { emitChatEvent } = require("../routes/chats.socket"); const { leaveChatRoom } = require("../auth/login"); const logger = require("../modules/logger"); const { From 095d12f63368ab0bdebeb8d852d75d96d24ef1b5 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 00:42:58 +0900 Subject: [PATCH 159/308] Rename: services --- src/routes/auth.js | 4 ++-- src/routes/auth.replace.js | 2 +- src/routes/chats.js | 2 +- src/routes/locations.js | 2 +- src/routes/logininfo.js | 2 +- src/routes/notifications.js | 2 +- src/routes/reports.js | 2 +- src/routes/rooms.js | 2 +- src/routes/users.js | 2 +- src/{service => services}/auth.js | 0 src/{service => services}/auth.mobile.js | 0 src/{service => services}/auth.replace.js | 2 +- src/{service => services}/chats.js | 0 src/{service => services}/locations.js | 0 src/{service => services}/logininfo.js | 0 src/{service => services}/notifications.js | 0 src/{service => services}/reports.js | 0 src/{service => services}/rooms.js | 0 src/{service => services}/users.js | 0 test/auth.replace.js | 2 +- test/locations.js | 2 +- test/logininfo.js | 2 +- test/reports.js | 2 +- test/rooms.js | 2 +- test/users.js | 2 +- 25 files changed, 17 insertions(+), 17 deletions(-) rename src/{service => services}/auth.js (100%) rename src/{service => services}/auth.mobile.js (100%) rename src/{service => services}/auth.replace.js (98%) rename src/{service => services}/chats.js (100%) rename src/{service => services}/locations.js (100%) rename src/{service => services}/logininfo.js (100%) rename src/{service => services}/notifications.js (100%) rename src/{service => services}/reports.js (100%) rename src/{service => services}/rooms.js (100%) rename src/{service => services}/users.js (100%) diff --git a/src/routes/auth.js b/src/routes/auth.js index c3f7e912..75d789e9 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -4,8 +4,8 @@ const { body } = require("express-validator"); const authMiddleware = require("../middlewares/auth"); const validator = require("../middlewares/validator"); -const authHandlers = require("../service/auth"); -const mobileAuthHandlers = require("../service/auth.mobile"); +const authHandlers = require("../services/auth"); +const mobileAuthHandlers = require("../services/auth.mobile"); const security = require("../../security"); const authReplace = require("./auth.replace"); diff --git a/src/routes/auth.replace.js b/src/routes/auth.replace.js index 3ca5f98c..8ea71de0 100755 --- a/src/routes/auth.replace.js +++ b/src/routes/auth.replace.js @@ -2,7 +2,7 @@ const express = require("express"); const router = express.Router(); const { body } = require("express-validator"); -const authReplaceHandlers = require("../service/auth.replace"); +const authReplaceHandlers = require("../services/auth.replace"); const authMiddleware = require("../middlewares/auth"); const validator = require("../middlewares/validator"); diff --git a/src/routes/chats.js b/src/routes/chats.js index 315f0dc1..fa8693c6 100644 --- a/src/routes/chats.js +++ b/src/routes/chats.js @@ -4,7 +4,7 @@ const validator = require("../middlewares/validator"); const patterns = require("../db/patterns"); const router = express.Router(); -const chatsHandlers = require("../service/chats"); +const chatsHandlers = require("../services/chats"); // 라우터 접근 시 로그인 필요 router.use(require("../middlewares/auth")); diff --git a/src/routes/locations.js b/src/routes/locations.js index 3e08bb97..90f1901d 100644 --- a/src/routes/locations.js +++ b/src/routes/locations.js @@ -1,7 +1,7 @@ const express = require("express"); const router = express.Router(); -const locationsHandlers = require("../service/locations"); +const locationsHandlers = require("../services/locations"); /** * @swagger diff --git a/src/routes/logininfo.js b/src/routes/logininfo.js index d221dd8c..b2e2b2db 100644 --- a/src/routes/logininfo.js +++ b/src/routes/logininfo.js @@ -1,7 +1,7 @@ const express = require("express"); const router = express.Router(); -const logininfoHandlers = require("../service/logininfo"); +const logininfoHandlers = require("../services/logininfo"); /** * @swagger diff --git a/src/routes/notifications.js b/src/routes/notifications.js index 044004b1..285368d6 100644 --- a/src/routes/notifications.js +++ b/src/routes/notifications.js @@ -2,7 +2,7 @@ const express = require("express"); const router = express.Router(); const { query, body } = require("express-validator"); -const notificationHandlers = require("../service/notifications"); +const notificationHandlers = require("../services/notifications"); const validator = require("../middlewares/validator"); // 라우터 접근 시 로그인 필요 diff --git a/src/routes/reports.js b/src/routes/reports.js index 19cc4ed5..bedc6a93 100644 --- a/src/routes/reports.js +++ b/src/routes/reports.js @@ -3,7 +3,7 @@ const { body } = require("express-validator"); const validator = require("../middlewares/validator"); const router = express.Router(); -const reportHandlers = require("../service/reports"); +const reportHandlers = require("../services/reports"); // 라우터 접근 시 로그인 필요 router.use(require("../middlewares/auth")); diff --git a/src/routes/rooms.js b/src/routes/rooms.js index 8750643d..9eefcf54 100644 --- a/src/routes/rooms.js +++ b/src/routes/rooms.js @@ -2,7 +2,7 @@ const express = require("express"); const { query, body } = require("express-validator"); const router = express.Router(); -const roomHandlers = require("../service/rooms"); +const roomHandlers = require("../services/rooms"); const validator = require("../middlewares/validator"); const patterns = require("../db/patterns"); diff --git a/src/routes/users.js b/src/routes/users.js index 07975b4a..556e9954 100755 --- a/src/routes/users.js +++ b/src/routes/users.js @@ -4,7 +4,7 @@ const validator = require("../middlewares/validator"); const patterns = require("../db/patterns"); const router = express.Router(); -const userHandlers = require("../service/users"); +const userHandlers = require("../services/users"); const { replaceSpaceInNickname } = require("../modules/modifyProfile"); diff --git a/src/service/auth.js b/src/services/auth.js similarity index 100% rename from src/service/auth.js rename to src/services/auth.js diff --git a/src/service/auth.mobile.js b/src/services/auth.mobile.js similarity index 100% rename from src/service/auth.mobile.js rename to src/services/auth.mobile.js diff --git a/src/service/auth.replace.js b/src/services/auth.replace.js similarity index 98% rename from src/service/auth.replace.js rename to src/services/auth.replace.js index e84c843e..35a47da0 100644 --- a/src/service/auth.replace.js +++ b/src/services/auth.replace.js @@ -12,7 +12,7 @@ const { } = require("../modules/modifyProfile"); const logger = require("../modules/logger"); -const { registerDeviceTokenHandler } = require("../service/auth"); +const { registerDeviceTokenHandler } = require("../services/auth"); const loginHtml = ` diff --git a/src/service/chats.js b/src/services/chats.js similarity index 100% rename from src/service/chats.js rename to src/services/chats.js diff --git a/src/service/locations.js b/src/services/locations.js similarity index 100% rename from src/service/locations.js rename to src/services/locations.js diff --git a/src/service/logininfo.js b/src/services/logininfo.js similarity index 100% rename from src/service/logininfo.js rename to src/services/logininfo.js diff --git a/src/service/notifications.js b/src/services/notifications.js similarity index 100% rename from src/service/notifications.js rename to src/services/notifications.js diff --git a/src/service/reports.js b/src/services/reports.js similarity index 100% rename from src/service/reports.js rename to src/services/reports.js diff --git a/src/service/rooms.js b/src/services/rooms.js similarity index 100% rename from src/service/rooms.js rename to src/services/rooms.js diff --git a/src/service/users.js b/src/services/users.js similarity index 100% rename from src/service/users.js rename to src/services/users.js diff --git a/test/auth.replace.js b/test/auth.replace.js index b3db78f1..eac2d6e4 100644 --- a/test/auth.replace.js +++ b/test/auth.replace.js @@ -1,6 +1,6 @@ const request = require("supertest"); -const authHandlers = require("../src/service/auth.replace"); +const authHandlers = require("../src/services/auth.replace"); const { userModel } = require("../src/db/mongo"); const security = require("../security"); diff --git a/test/locations.js b/test/locations.js index 1362f9ce..19308e51 100644 --- a/test/locations.js +++ b/test/locations.js @@ -1,5 +1,5 @@ const expect = require("chai").expect; -const locationHandlers = require("../src/service/locations"); +const locationHandlers = require("../src/services/locations"); const httpMocks = require("node-mocks-http"); // locations.js 관련 1개의 handler을 테스트 diff --git a/test/logininfo.js b/test/logininfo.js index f2f19a2c..0abee30b 100644 --- a/test/logininfo.js +++ b/test/logininfo.js @@ -1,5 +1,5 @@ const expect = require("chai").expect; -const logininfoHandlers = require("../src/service/logininfo"); +const logininfoHandlers = require("../src/services/logininfo"); const { userModel } = require("../src/db/mongo"); // logininfo.js 관련 2개의 handler을 테스트 (동기식이므로 httpMocks 사용하지 않음) diff --git a/test/reports.js b/test/reports.js index 9c253277..07c3ebeb 100644 --- a/test/reports.js +++ b/test/reports.js @@ -1,5 +1,5 @@ const expect = require("chai").expect; -const reportHandlers = require("../src/service/reports"); +const reportHandlers = require("../src/services/reports"); const { userModel } = require("../src/db/mongo"); const { userGenerator, testRemover } = require("./utils"); const httpMocks = require("node-mocks-http"); diff --git a/test/rooms.js b/test/rooms.js index 0ee98b62..c68248b4 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -1,6 +1,6 @@ const expect = require("chai").expect; const express = require("express"); -const roomsHandlers = require("../src/service/rooms"); +const roomsHandlers = require("../src/services/rooms"); const { userModel, roomModel, locationModel } = require("../src/db/mongo"); const { userGenerator, testRemover } = require("./utils"); const app = express(); diff --git a/test/users.js b/test/users.js index aa78f26a..f22285af 100644 --- a/test/users.js +++ b/test/users.js @@ -1,5 +1,5 @@ const expect = require("chai").expect; -const usersHandlers = require("../src/service/users"); +const usersHandlers = require("../src/services/users"); const { userModel } = require("../src/db/mongo"); const { userGenerator, testRemover } = require("./utils"); const httpMocks = require("node-mocks-http"); From b9c3473883836a50a49b46a4a5b8ece288723b8d Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 00:46:15 +0900 Subject: [PATCH 160/308] Move: socket.chats.js --- src/modules/socket.js | 2 +- src/services/chats.js | 2 +- src/services/rooms.js | 2 +- src/{routes/chats.socket.js => services/socket.chats.js} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/{routes/chats.socket.js => services/socket.chats.js} (100%) diff --git a/src/modules/socket.js b/src/modules/socket.js index 71a4f992..4df0d647 100644 --- a/src/modules/socket.js +++ b/src/modules/socket.js @@ -2,7 +2,7 @@ const { Server } = require("socket.io"); const sharedsession = require("express-socket.io-session"); const cookieParser = require("cookie-parser"); const security = require("../../security"); -const { ioListeners } = require("../routes/chats.socket"); +const { ioListeners } = require("../services/socket.chats"); // server: express server // session: session middleware diff --git a/src/services/chats.js b/src/services/chats.js index d938214b..199dbff2 100644 --- a/src/services/chats.js +++ b/src/services/chats.js @@ -1,7 +1,7 @@ const { chatModel, userModel } = require("../db/mongo"); const awsS3 = require("../db/awsS3"); -const { emitChatEvent } = require("../routes/chats.socket"); +const { emitChatEvent } = require("./socket.chats"); const uploadChatImgGetPUrlHandler = async (req, res) => { try { diff --git a/src/services/rooms.js b/src/services/rooms.js index a8b93730..fb23af8d 100644 --- a/src/services/rooms.js +++ b/src/services/rooms.js @@ -1,5 +1,5 @@ const { roomModel, locationModel, userModel } = require("../db/mongo"); -const { emitChatEvent } = require("../routes/chats.socket"); +const { emitChatEvent } = require("./socket.chats"); const { leaveChatRoom } = require("../auth/login"); const logger = require("../modules/logger"); const { diff --git a/src/routes/chats.socket.js b/src/services/socket.chats.js similarity index 100% rename from src/routes/chats.socket.js rename to src/services/socket.chats.js From 5521221c80cac17c1ea045ff08dd34501e3f1339 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 00:58:01 +0900 Subject: [PATCH 161/308] Rename: loadenv --- app.js | 6 +++--- security.js => loadenv.js | 0 src/config/secretKey.js | 2 +- src/db/awsS3.js | 14 +++++++------- src/db/mongo.js | 6 +++--- src/middlewares/authAdmin.js | 2 +- src/middlewares/session.js | 10 +++++----- src/modules/fcm.js | 2 +- src/modules/logger.js | 2 +- src/modules/socket.js | 4 ++-- src/routes/auth.js | 4 ++-- src/services/auth.js | 17 ++++++++--------- src/services/auth.replace.js | 6 +++--- test/auth.replace.js | 6 +++--- 14 files changed, 40 insertions(+), 41 deletions(-) rename security.js => loadenv.js (100%) diff --git a/app.js b/app.js index fb3274c5..d765e5aa 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,7 @@ // 모듈 require const express = require("express"); const http = require("http"); -const security = require("./security"); +const loadenv = require("./loadenv"); const logger = require("./src/modules/logger"); const logAPIAccess = require("./src/modules/logAPIAccess"); const startSocketServer = require("./src/modules/socket"); @@ -52,8 +52,8 @@ app.use("/notifications", require("./src/routes/notifications")); // express 서버 시작 const serverHttp = http .createServer(app) - .listen(security.port, () => - logger.info(`Express 서버가 ${security.port}번 포트에서 시작됨.`) + .listen(loadenv.port, () => + logger.info(`Express 서버가 ${loadenv.port}번 포트에서 시작됨.`) ); // socket.io 서버 시작 및 app 인스턴스에 저장 diff --git a/security.js b/loadenv.js similarity index 100% rename from security.js rename to loadenv.js diff --git a/src/config/secretKey.js b/src/config/secretKey.js index bbd50ce1..ce6c3928 100644 --- a/src/config/secretKey.js +++ b/src/config/secretKey.js @@ -1,4 +1,4 @@ -const { jwtSecretKey, frontUrl } = require("../../security"); +const { jwtSecretKey, frontUrl } = require("../../loadenv"); module.exports = { jwtSecretKey: jwtSecretKey, diff --git a/src/db/awsS3.js b/src/db/awsS3.js index 9fb05bc1..7c829aa5 100644 --- a/src/db/awsS3.js +++ b/src/db/awsS3.js @@ -1,4 +1,4 @@ -const security = require("../../security"); +const loadenv = require("../../loadenv"); // Load the AWS-SDK and s3 const AWS = require("aws-sdk"); @@ -12,7 +12,7 @@ const s3 = new AWS.S3({ apiVersion: "2006-03-01" }); module.exports.getList = (directoryPath, cb) => { s3.listObjects( { - Bucket: security.aws.s3BucketName, + Bucket: loadenv.aws.s3BucketName, Prefix: directoryPath, }, (err, data) => { @@ -24,7 +24,7 @@ module.exports.getList = (directoryPath, cb) => { // function to generate signed-url for upload(PUT) module.exports.getUploadPUrlPut = (filePath, contentType = "image/png") => { const presignedUrl = s3.getSignedUrl("putObject", { - Bucket: security.aws.s3BucketName, + Bucket: loadenv.aws.s3BucketName, Key: filePath, ContentType: contentType, Expires: 60, // 1 min @@ -36,7 +36,7 @@ module.exports.getUploadPUrlPut = (filePath, contentType = "image/png") => { module.exports.getUploadPUrlPost = (filePath, contentType, cb) => { s3.createPresignedPost( { - Bucket: security.aws.s3BucketName, + Bucket: loadenv.aws.s3BucketName, Expires: 60, // 1 min Conditions: [ { key: filePath }, @@ -54,7 +54,7 @@ module.exports.getUploadPUrlPost = (filePath, contentType, cb) => { module.exports.deleteObject = (filePath, cb) => { s3.deleteObject( { - Bucket: security.aws.s3BucketName, + Bucket: loadenv.aws.s3BucketName, Key: filePath, }, (err, data) => { @@ -67,7 +67,7 @@ module.exports.deleteObject = (filePath, cb) => { module.exports.foundObject = (filePath, cb) => { s3.headObject( { - Bucket: security.aws.s3BucketName, + Bucket: loadenv.aws.s3BucketName, Key: filePath, }, (err, data) => { @@ -78,5 +78,5 @@ module.exports.foundObject = (filePath, cb) => { // function to return full URL of the object module.exports.getS3Url = (filePath) => { - return `${security.aws.s3Url}${filePath}`; + return `${loadenv.aws.s3Url}${filePath}`; }; diff --git a/src/db/mongo.js b/src/db/mongo.js index 8cc60b25..2963d1b6 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -1,7 +1,7 @@ const mongoose = require("mongoose"); const Schema = mongoose.Schema; -const security = require("../../security"); +const loadenv = require("../../loadenv"); const logger = require("../modules/logger"); const userSchema = Schema({ @@ -172,14 +172,14 @@ database.on("disconnected", function () { // 데이터베이스 연결이 끊어지면 5초 후 재연결을 시도합니다. logger.error("데이터베이스와 연결이 끊어졌습니다!"); setTimeout(() => { - mongoose.connect(security.mongo, { + mongoose.connect(loadenv.mongo, { useNewUrlParser: true, useUnifiedTopology: true, }); }, 5000); }); -mongoose.connect(security.mongo, { +mongoose.connect(loadenv.mongo, { useNewUrlParser: true, useUnifiedTopology: true, }); diff --git a/src/middlewares/authAdmin.js b/src/middlewares/authAdmin.js index 1a335b89..06fef0d1 100644 --- a/src/middlewares/authAdmin.js +++ b/src/middlewares/authAdmin.js @@ -1,7 +1,7 @@ // 관리자 유무를 확인하기 위한 미들웨어입니다. const { isLogin, getLoginInfo } = require("../auth/login"); -const { frontUrl } = require("../../security"); +const { frontUrl } = require("../../loadenv"); const { userModel, adminIPWhitelistModel } = require("../db/mongo"); const authAdminMiddleware = async (req, res, next) => { diff --git a/src/middlewares/session.js b/src/middlewares/session.js index 5dd342de..cdfc0f0a 100644 --- a/src/middlewares/session.js +++ b/src/middlewares/session.js @@ -2,25 +2,25 @@ const expressSession = require("express-session"); const redis = require("redis"); const MongoStore = require("connect-mongo"); const RedisStore = require("connect-redis")(expressSession); -const security = require("../../security"); +const loadenv = require("../../loadenv"); const logger = require("../modules/logger"); // 환경변수 REDIS_PATH 유무에 따라 session 저장 방식이 변경됩니다. let sessionStore = null; -if (security.redis) { +if (loadenv.redis) { const client = redis.createClient({ - url: security.redis, + url: loadenv.redis, legacyMode: true, }); client.connect().catch(logger.error); sessionStore = new RedisStore({ client }); } else { - const mongoUrl = security.mongo; + const mongoUrl = loadenv.mongo; sessionStore = MongoStore.create({ mongoUrl }); } module.exports = expressSession({ - secret: security.session, + secret: loadenv.session, resave: true, saveUninitialized: true, store: sessionStore, diff --git a/src/modules/fcm.js b/src/modules/fcm.js index e569392b..feabfa12 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -6,7 +6,7 @@ const { topicSubscriptionModel, } = require("../db/mongo"); const logger = require("../modules/logger"); -const { googleApplicationCredentials } = require("../../security"); +const { googleApplicationCredentials } = require("../../loadenv"); /** * credential을 등록합니다. diff --git a/src/modules/logger.js b/src/modules/logger.js index 94ab8f96..0e02e56e 100644 --- a/src/modules/logger.js +++ b/src/modules/logger.js @@ -2,7 +2,7 @@ const { createLogger, format, transports } = require("winston"); const dailyRotateFileTransport = require("winston-daily-rotate-file"); const path = require("path"); -const { nodeEnv } = require("../../security"); +const { nodeEnv } = require("../../loadenv"); // 로깅에 사용하기 위한 포맷을 추가로 정의합니다. const customFormat = { diff --git a/src/modules/socket.js b/src/modules/socket.js index 4df0d647..ae12831b 100644 --- a/src/modules/socket.js +++ b/src/modules/socket.js @@ -1,7 +1,7 @@ const { Server } = require("socket.io"); const sharedsession = require("express-socket.io-session"); const cookieParser = require("cookie-parser"); -const security = require("../../security"); +const loadenv = require("../../loadenv"); const { ioListeners } = require("../services/socket.chats"); // server: express server @@ -9,7 +9,7 @@ const { ioListeners } = require("../services/socket.chats"); module.exports = (server, session) => { const io = new Server(server, { cors: { - origin: [security.frontUrl], + origin: [loadenv.frontUrl], methods: ["GET", "POST"], credentials: true, }, diff --git a/src/routes/auth.js b/src/routes/auth.js index 75d789e9..885297bc 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -7,7 +7,7 @@ const validator = require("../middlewares/validator"); const authHandlers = require("../services/auth"); const mobileAuthHandlers = require("../services/auth.mobile"); -const security = require("../../security"); +const loadenv = require("../../loadenv"); const authReplace = require("./auth.replace"); router.route("/sparcssso").get(authHandlers.sparcsssoHandler); @@ -29,4 +29,4 @@ router.post( ); // 환경변수 SPARCSSSO_CLIENT_ID 유무에 따라 로그인 방식이 변경됩니다. -module.exports = security.sparcssso?.id ? router : authReplace; +module.exports = loadenv.sparcssso?.id ? router : authReplace; diff --git a/src/services/auth.js b/src/services/auth.js index 0c76b020..4d79ee55 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -1,4 +1,4 @@ -const security = require("../../security"); +const loadenv = require("../../loadenv"); const { userModel } = require("../db/mongo"); const { user: userPattern } = require("../db/patterns"); const { getLoginInfo, logout, login } = require("../auth/login"); @@ -15,11 +15,10 @@ const { getFullUsername, } = require("../modules/modifyProfile"); const jwt = require("../modules/jwt"); -const APP_URI_SCHEME = require("../../security").appUriScheme; // SPARCS SSO const Client = require("../auth/sparcsso"); -const client = new Client(security.sparcssso?.id, security.sparcssso?.key); +const client = new Client(loadenv.sparcssso?.id, loadenv.sparcssso?.key); const transUserData = (userData) => { const kaistInfo = userData.kaist_info ? JSON.parse(userData.kaist_info) : {}; @@ -81,14 +80,14 @@ const loginDone = (req, res, userData) => { else if (result.name != userData.name) update(req, res, userData); else { login(req, userData.sid, result.id, result.name); - res.redirect(security.frontUrl + "/"); + res.redirect(loadenv.frontUrl + "/"); } } ); }; const loginFail = (req, res, redirectUrl = "") => { - res.redirect(redirectUrl || security.frontUrl + "/login/fail"); + res.redirect(redirectUrl || loadenv.frontUrl + "/login/fail"); }; const generateTokenHandler = (req, res) => { @@ -111,7 +110,7 @@ const sparcsssoCallbackHandler = (req, res) => { const code = req.body.code || req.query.code; client.getUserInfo(code).then((userDataBefore) => { const userData = transUserData(userDataBefore); - if (userData.isEligible || security.nodeEnv !== "production") { + if (userData.isEligible || loadenv.nodeEnv !== "production") { if (req.session.isApp) { createNewTokenHandler(req, res, userData); } else { @@ -120,7 +119,7 @@ const sparcsssoCallbackHandler = (req, res) => { } else { // 카이스트 구성원이 아닌 경우, SSO 로그아웃 이후, 로그인 실패 URI 로 이동합니다 const { sid } = userData; - const redirectUrl = security.frontUrl + "/login/fail"; + const redirectUrl = loadenv.frontUrl + "/login/fail"; const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); loginFail(req, res, ssoLogoutUrl); } @@ -150,7 +149,7 @@ const createNewTokenHandler = (req, res, userData) => { type: "refresh", }); res.redirect( - APP_URI_SCHEME + + loadenv.appUriScheme + "://login?accessToken=" + accessToken.token + "&refreshToken=" + @@ -171,7 +170,7 @@ const logoutHandler = async (req, res) => { await unregisterDeviceToken(deviceToken); } - const redirectUrl = security.frontUrl + "/login"; + const redirectUrl = loadenv.frontUrl + "/login"; const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); logout(req, res); res.json({ ssoLogoutUrl }); diff --git a/src/services/auth.replace.js b/src/services/auth.replace.js index 35a47da0..3ec2ef28 100644 --- a/src/services/auth.replace.js +++ b/src/services/auth.replace.js @@ -1,4 +1,4 @@ -const security = require("../../security"); +const { frontUrl } = require("../../loadenv"); const { userModel } = require("../db/mongo"); const { getLoginInfo, logout, login } = require("../auth/login"); @@ -113,7 +113,7 @@ const loginDone = (req, res, userData) => { else { login(req, userData.sid, result.id, result.name); // res.send("successful"); //API 테스트용 코드(프론트 리다이렉트 X) - res.redirect(security.frontUrl); + res.redirect(frontUrl); } } ); @@ -138,7 +138,7 @@ const logoutHandler = async (req, res) => { await unregisterDeviceToken(deviceToken); } - const ssoLogoutUrl = security.frontUrl + "/login"; + const ssoLogoutUrl = frontUrl + "/login"; logout(req, res); res.json({ ssoLogoutUrl }); } catch (e) { diff --git a/test/auth.replace.js b/test/auth.replace.js index eac2d6e4..913aa906 100644 --- a/test/auth.replace.js +++ b/test/auth.replace.js @@ -2,7 +2,7 @@ const request = require("supertest"); const authHandlers = require("../src/services/auth.replace"); const { userModel } = require("../src/db/mongo"); -const security = require("../security"); +const { frontUrl } = require("../loadenv"); // auth.replace.js 관련 1개의 handler을 테스트 // 1. dev 환경에서 front의 URL로 잘 redirect 되는지 확인 @@ -14,14 +14,14 @@ describe("[auth.replace] 1.sparcsssoHandler", () => { before(removeTestUser); - it("should redirect to security.frontUrl after successful user creation", () => { + it("should redirect to frontUrl after successful user creation", () => { request(authHandlers.sparcsssoHandler) .post("/auth/sparcssso") .send({ id: "test", }) .expect(302) - .expect("Location", security.frontUrl); + .expect("Location", frontUrl); }); after(removeTestUser); From eca2c8206bdf6379835ef1a74999d7d76ea9de99 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 01:07:54 +0900 Subject: [PATCH 162/308] Refactor: import loadenv --- app.js | 6 +++--- src/db/awsS3.js | 14 +++++++------- src/db/mongo.js | 6 +++--- src/middlewares/session.js | 13 ++++++++----- src/modules/socket.js | 4 ++-- src/routes/auth.js | 4 ++-- src/services/auth.js | 21 +++++++++++++-------- 7 files changed, 38 insertions(+), 30 deletions(-) diff --git a/app.js b/app.js index d765e5aa..744bb7c8 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,7 @@ // 모듈 require const express = require("express"); const http = require("http"); -const loadenv = require("./loadenv"); +const { port: httpPort } = require("./loadenv"); const logger = require("./src/modules/logger"); const logAPIAccess = require("./src/modules/logAPIAccess"); const startSocketServer = require("./src/modules/socket"); @@ -52,8 +52,8 @@ app.use("/notifications", require("./src/routes/notifications")); // express 서버 시작 const serverHttp = http .createServer(app) - .listen(loadenv.port, () => - logger.info(`Express 서버가 ${loadenv.port}번 포트에서 시작됨.`) + .listen(httpPort, () => + logger.info(`Express 서버가 ${httpPort}번 포트에서 시작됨.`) ); // socket.io 서버 시작 및 app 인스턴스에 저장 diff --git a/src/db/awsS3.js b/src/db/awsS3.js index 7c829aa5..f51f6dc1 100644 --- a/src/db/awsS3.js +++ b/src/db/awsS3.js @@ -1,4 +1,4 @@ -const loadenv = require("../../loadenv"); +const { aws: awsEnv } = require("../../loadenv"); // Load the AWS-SDK and s3 const AWS = require("aws-sdk"); @@ -12,7 +12,7 @@ const s3 = new AWS.S3({ apiVersion: "2006-03-01" }); module.exports.getList = (directoryPath, cb) => { s3.listObjects( { - Bucket: loadenv.aws.s3BucketName, + Bucket: awsEnv.s3BucketName, Prefix: directoryPath, }, (err, data) => { @@ -24,7 +24,7 @@ module.exports.getList = (directoryPath, cb) => { // function to generate signed-url for upload(PUT) module.exports.getUploadPUrlPut = (filePath, contentType = "image/png") => { const presignedUrl = s3.getSignedUrl("putObject", { - Bucket: loadenv.aws.s3BucketName, + Bucket: awsEnv.s3BucketName, Key: filePath, ContentType: contentType, Expires: 60, // 1 min @@ -36,7 +36,7 @@ module.exports.getUploadPUrlPut = (filePath, contentType = "image/png") => { module.exports.getUploadPUrlPost = (filePath, contentType, cb) => { s3.createPresignedPost( { - Bucket: loadenv.aws.s3BucketName, + Bucket: awsEnv.s3BucketName, Expires: 60, // 1 min Conditions: [ { key: filePath }, @@ -54,7 +54,7 @@ module.exports.getUploadPUrlPost = (filePath, contentType, cb) => { module.exports.deleteObject = (filePath, cb) => { s3.deleteObject( { - Bucket: loadenv.aws.s3BucketName, + Bucket: awsEnv.s3BucketName, Key: filePath, }, (err, data) => { @@ -67,7 +67,7 @@ module.exports.deleteObject = (filePath, cb) => { module.exports.foundObject = (filePath, cb) => { s3.headObject( { - Bucket: loadenv.aws.s3BucketName, + Bucket: awsEnv.s3BucketName, Key: filePath, }, (err, data) => { @@ -78,5 +78,5 @@ module.exports.foundObject = (filePath, cb) => { // function to return full URL of the object module.exports.getS3Url = (filePath) => { - return `${loadenv.aws.s3Url}${filePath}`; + return `${awsEnv.s3Url}${filePath}`; }; diff --git a/src/db/mongo.js b/src/db/mongo.js index 2963d1b6..928f135e 100755 --- a/src/db/mongo.js +++ b/src/db/mongo.js @@ -1,7 +1,7 @@ const mongoose = require("mongoose"); const Schema = mongoose.Schema; -const loadenv = require("../../loadenv"); +const { mongo: mongoUrl } = require("../../loadenv"); const logger = require("../modules/logger"); const userSchema = Schema({ @@ -172,14 +172,14 @@ database.on("disconnected", function () { // 데이터베이스 연결이 끊어지면 5초 후 재연결을 시도합니다. logger.error("데이터베이스와 연결이 끊어졌습니다!"); setTimeout(() => { - mongoose.connect(loadenv.mongo, { + mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true, }); }, 5000); }); -mongoose.connect(loadenv.mongo, { +mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true, }); diff --git a/src/middlewares/session.js b/src/middlewares/session.js index cdfc0f0a..a28be214 100644 --- a/src/middlewares/session.js +++ b/src/middlewares/session.js @@ -2,25 +2,28 @@ const expressSession = require("express-session"); const redis = require("redis"); const MongoStore = require("connect-mongo"); const RedisStore = require("connect-redis")(expressSession); -const loadenv = require("../../loadenv"); +const { + redis: redisUrl, + mongo: mongoUrl, + session: sessionSecret, +} = require("../../loadenv"); const logger = require("../modules/logger"); // 환경변수 REDIS_PATH 유무에 따라 session 저장 방식이 변경됩니다. let sessionStore = null; -if (loadenv.redis) { +if (redisUrl) { const client = redis.createClient({ - url: loadenv.redis, + url: redisUrl, legacyMode: true, }); client.connect().catch(logger.error); sessionStore = new RedisStore({ client }); } else { - const mongoUrl = loadenv.mongo; sessionStore = MongoStore.create({ mongoUrl }); } module.exports = expressSession({ - secret: loadenv.session, + secret: sessionSecret, resave: true, saveUninitialized: true, store: sessionStore, diff --git a/src/modules/socket.js b/src/modules/socket.js index ae12831b..6e7bbd25 100644 --- a/src/modules/socket.js +++ b/src/modules/socket.js @@ -1,7 +1,7 @@ const { Server } = require("socket.io"); const sharedsession = require("express-socket.io-session"); const cookieParser = require("cookie-parser"); -const loadenv = require("../../loadenv"); +const { frontUrl } = require("../../loadenv"); const { ioListeners } = require("../services/socket.chats"); // server: express server @@ -9,7 +9,7 @@ const { ioListeners } = require("../services/socket.chats"); module.exports = (server, session) => { const io = new Server(server, { cors: { - origin: [loadenv.frontUrl], + origin: [frontUrl], methods: ["GET", "POST"], credentials: true, }, diff --git a/src/routes/auth.js b/src/routes/auth.js index 885297bc..58d3bcbd 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -7,7 +7,7 @@ const validator = require("../middlewares/validator"); const authHandlers = require("../services/auth"); const mobileAuthHandlers = require("../services/auth.mobile"); -const loadenv = require("../../loadenv"); +const { sparcssso: sparcsssoEnv } = require("../../loadenv"); const authReplace = require("./auth.replace"); router.route("/sparcssso").get(authHandlers.sparcsssoHandler); @@ -29,4 +29,4 @@ router.post( ); // 환경변수 SPARCSSSO_CLIENT_ID 유무에 따라 로그인 방식이 변경됩니다. -module.exports = loadenv.sparcssso?.id ? router : authReplace; +module.exports = sparcsssoEnv?.id ? router : authReplace; diff --git a/src/services/auth.js b/src/services/auth.js index 4d79ee55..7af96794 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -1,4 +1,9 @@ -const loadenv = require("../../loadenv"); +const { + sparcssso: sparcsssoEnv, + frontUrl, + nodeEnv, + appUriScheme, +} = require("../../loadenv"); const { userModel } = require("../db/mongo"); const { user: userPattern } = require("../db/patterns"); const { getLoginInfo, logout, login } = require("../auth/login"); @@ -18,7 +23,7 @@ const jwt = require("../modules/jwt"); // SPARCS SSO const Client = require("../auth/sparcsso"); -const client = new Client(loadenv.sparcssso?.id, loadenv.sparcssso?.key); +const client = new Client(sparcsssoEnv?.id, sparcsssoEnv?.key); const transUserData = (userData) => { const kaistInfo = userData.kaist_info ? JSON.parse(userData.kaist_info) : {}; @@ -80,14 +85,14 @@ const loginDone = (req, res, userData) => { else if (result.name != userData.name) update(req, res, userData); else { login(req, userData.sid, result.id, result.name); - res.redirect(loadenv.frontUrl + "/"); + res.redirect(frontUrl + "/"); } } ); }; const loginFail = (req, res, redirectUrl = "") => { - res.redirect(redirectUrl || loadenv.frontUrl + "/login/fail"); + res.redirect(redirectUrl || frontUrl + "/login/fail"); }; const generateTokenHandler = (req, res) => { @@ -110,7 +115,7 @@ const sparcsssoCallbackHandler = (req, res) => { const code = req.body.code || req.query.code; client.getUserInfo(code).then((userDataBefore) => { const userData = transUserData(userDataBefore); - if (userData.isEligible || loadenv.nodeEnv !== "production") { + if (userData.isEligible || nodeEnv !== "production") { if (req.session.isApp) { createNewTokenHandler(req, res, userData); } else { @@ -119,7 +124,7 @@ const sparcsssoCallbackHandler = (req, res) => { } else { // 카이스트 구성원이 아닌 경우, SSO 로그아웃 이후, 로그인 실패 URI 로 이동합니다 const { sid } = userData; - const redirectUrl = loadenv.frontUrl + "/login/fail"; + const redirectUrl = frontUrl + "/login/fail"; const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); loginFail(req, res, ssoLogoutUrl); } @@ -149,7 +154,7 @@ const createNewTokenHandler = (req, res, userData) => { type: "refresh", }); res.redirect( - loadenv.appUriScheme + + appUriScheme + "://login?accessToken=" + accessToken.token + "&refreshToken=" + @@ -170,7 +175,7 @@ const logoutHandler = async (req, res) => { await unregisterDeviceToken(deviceToken); } - const redirectUrl = loadenv.frontUrl + "/login"; + const redirectUrl = frontUrl + "/login"; const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); logout(req, res); res.json({ ssoLogoutUrl }); From e42b35c51fdf30ffee9d56ee8d0ee3a64ed60746 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 01:15:13 +0900 Subject: [PATCH 163/308] Remove: secretKey.js --- loadenv.js | 8 +++++++- src/config/secretKey.js | 9 --------- src/modules/jwt.js | 6 +++--- src/services/auth.mobile.js | 1 - 4 files changed, 10 insertions(+), 14 deletions(-) delete mode 100644 src/config/secretKey.js diff --git a/loadenv.js b/loadenv.js index 9b947f16..56d43644 100644 --- a/loadenv.js +++ b/loadenv.js @@ -20,7 +20,13 @@ module.exports = { process.env.AWS_S3_URL || "https://${process.env.AWS_S3_BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com", // optional }, - jwtSecretKey: process.env.JWT_SECRET_KEY, + jwt: { + secretKey: process.env.JWT_SECRET_KEY, + option: { + algorithm: "HS256", + issuer: process.env.FRONT_URL || "http://localhost:3000", // optional (default = "http://localhost:3000") + }, + }, appUriScheme: process.env.APP_URI_SCHEME, googleApplicationCredentials: process.env.GOOGLE_APPLICATION_CREDENTIALS && diff --git a/src/config/secretKey.js b/src/config/secretKey.js deleted file mode 100644 index ce6c3928..00000000 --- a/src/config/secretKey.js +++ /dev/null @@ -1,9 +0,0 @@ -const { jwtSecretKey, frontUrl } = require("../../loadenv"); - -module.exports = { - jwtSecretKey: jwtSecretKey, - option: { - algorithm: "HS256", - issuer: frontUrl, - }, -}; diff --git a/src/modules/jwt.js b/src/modules/jwt.js index 8931e724..453d237b 100644 --- a/src/modules/jwt.js +++ b/src/modules/jwt.js @@ -1,5 +1,5 @@ const jwt = require("jsonwebtoken"); -const { jwtSecretKey, option } = require("../config/secretKey"); +const { secretKey, option } = require("../../loadenv").jwt; const { TOKEN_EXPIRED, TOKEN_INVALID } = require("../config/constants"); const signJwt = async ({ id, type }) => { @@ -18,7 +18,7 @@ const signJwt = async ({ id, type }) => { } const result = { - token: jwt.sign(payload, jwtSecretKey, options), + token: jwt.sign(payload, secretKey, options), }; return result; }; @@ -26,7 +26,7 @@ const signJwt = async ({ id, type }) => { const verifyJwt = async (token) => { let decoded; try { - decoded = jwt.verify(token, jwtSecretKey); + decoded = jwt.verify(token, secretKey); } catch (err) { if (err.message === "jwt expired") { return TOKEN_EXPIRED; diff --git a/src/services/auth.mobile.js b/src/services/auth.mobile.js index e4e07b2c..fea282e5 100644 --- a/src/services/auth.mobile.js +++ b/src/services/auth.mobile.js @@ -1,5 +1,4 @@ const { userModel } = require("../db/mongo"); -const { deviceTokenModel } = require("../db/mongo"); const { login } = require("../auth/login"); const { From a886480cb4388b4db75ea9f270298c8c85040729 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 01:18:11 +0900 Subject: [PATCH 164/308] Remove: constants.js --- loadenv.js | 2 ++ src/config/constants.js | 7 ------- src/modules/jwt.js | 4 ++-- src/services/auth.mobile.js | 2 +- 4 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 src/config/constants.js diff --git a/loadenv.js b/loadenv.js index 56d43644..a8aa9daf 100644 --- a/loadenv.js +++ b/loadenv.js @@ -26,6 +26,8 @@ module.exports = { algorithm: "HS256", issuer: process.env.FRONT_URL || "http://localhost:3000", // optional (default = "http://localhost:3000") }, + TOKEN_EXPIRED: -3, + TOKEN_INVALID: -2, }, appUriScheme: process.env.APP_URI_SCHEME, googleApplicationCredentials: diff --git a/src/config/constants.js b/src/config/constants.js deleted file mode 100644 index 2e8d139d..00000000 --- a/src/config/constants.js +++ /dev/null @@ -1,7 +0,0 @@ -const TOKEN_EXPIRED = -3; -const TOKEN_INVALID = -2; - -module.exports = { - TOKEN_INVALID, - TOKEN_EXPIRED, -}; diff --git a/src/modules/jwt.js b/src/modules/jwt.js index 453d237b..079b01f0 100644 --- a/src/modules/jwt.js +++ b/src/modules/jwt.js @@ -1,6 +1,6 @@ const jwt = require("jsonwebtoken"); -const { secretKey, option } = require("../../loadenv").jwt; -const { TOKEN_EXPIRED, TOKEN_INVALID } = require("../config/constants"); +const { secretKey, option, TOKEN_EXPIRED, TOKEN_INVALID } = + require("../../loadenv").jwt; const signJwt = async ({ id, type }) => { const payload = { diff --git a/src/services/auth.mobile.js b/src/services/auth.mobile.js index fea282e5..a1940088 100644 --- a/src/services/auth.mobile.js +++ b/src/services/auth.mobile.js @@ -8,7 +8,7 @@ const { const jwt = require("../modules/jwt"); const logger = require("../modules/logger"); -const { TOKEN_EXPIRED, TOKEN_INVALID } = require("../config/constants"); +const { TOKEN_EXPIRED, TOKEN_INVALID } = require("../../loadenv").jwt; const loginWithToken = async (req, res) => { req.session.isApp = true; From 92217d6262eed17751939ab983a0f336d3621916 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 01:27:27 +0900 Subject: [PATCH 165/308] Move: modules/stores --- src/middlewares/authAdmin.js | 2 +- src/modules/fcm.js | 2 +- src/{db => modules/stores}/awsS3.js | 2 +- src/{db => modules/stores}/mongo.js | 4 ++-- src/routes/admin.js | 2 +- src/services/auth.js | 2 +- src/services/auth.mobile.js | 2 +- src/services/auth.replace.js | 4 ++-- src/services/chats.js | 4 ++-- src/services/locations.js | 2 +- src/services/logininfo.js | 2 +- src/services/notifications.js | 2 +- src/services/reports.js | 2 +- src/services/rooms.js | 6 +++++- src/services/socket.chats.js | 4 ++-- src/services/users.js | 16 ++++++++++------ test/auth.replace.js | 2 +- test/logininfo.js | 2 +- test/reports.js | 2 +- test/rooms.js | 6 +++++- test/users.js | 2 +- test/utils.js | 2 +- 22 files changed, 43 insertions(+), 31 deletions(-) rename src/{db => modules/stores}/awsS3.js (97%) rename src/{db => modules/stores}/mongo.js (98%) diff --git a/src/middlewares/authAdmin.js b/src/middlewares/authAdmin.js index 06fef0d1..8919cf96 100644 --- a/src/middlewares/authAdmin.js +++ b/src/middlewares/authAdmin.js @@ -2,7 +2,7 @@ const { isLogin, getLoginInfo } = require("../auth/login"); const { frontUrl } = require("../../loadenv"); -const { userModel, adminIPWhitelistModel } = require("../db/mongo"); +const { userModel, adminIPWhitelistModel } = require("../modules/stores/mongo"); const authAdminMiddleware = async (req, res, next) => { try { diff --git a/src/modules/fcm.js b/src/modules/fcm.js index feabfa12..8d30d156 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -4,7 +4,7 @@ const { deviceTokenModel, notificationOptionModel, topicSubscriptionModel, -} = require("../db/mongo"); +} = require("./stores/mongo"); const logger = require("../modules/logger"); const { googleApplicationCredentials } = require("../../loadenv"); diff --git a/src/db/awsS3.js b/src/modules/stores/awsS3.js similarity index 97% rename from src/db/awsS3.js rename to src/modules/stores/awsS3.js index f51f6dc1..ccdac2c8 100644 --- a/src/db/awsS3.js +++ b/src/modules/stores/awsS3.js @@ -1,4 +1,4 @@ -const { aws: awsEnv } = require("../../loadenv"); +const { aws: awsEnv } = require("../../../loadenv"); // Load the AWS-SDK and s3 const AWS = require("aws-sdk"); diff --git a/src/db/mongo.js b/src/modules/stores/mongo.js similarity index 98% rename from src/db/mongo.js rename to src/modules/stores/mongo.js index 928f135e..f9b49e84 100755 --- a/src/db/mongo.js +++ b/src/modules/stores/mongo.js @@ -1,8 +1,8 @@ const mongoose = require("mongoose"); const Schema = mongoose.Schema; -const { mongo: mongoUrl } = require("../../loadenv"); -const logger = require("../modules/logger"); +const { mongo: mongoUrl } = require("../../../loadenv"); +const logger = require("../logger"); const userSchema = Schema({ name: { type: String, required: true }, //실명 diff --git a/src/routes/admin.js b/src/routes/admin.js index 4d5cf65a..363e1282 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -13,7 +13,7 @@ const { adminLogModel, deviceTokenModel, notificationOptionModel, -} = require("../db/mongo"); +} = require("../modules/stores/mongo"); const router = express.Router(); diff --git a/src/services/auth.js b/src/services/auth.js index 7af96794..6d3a1755 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -4,7 +4,7 @@ const { nodeEnv, appUriScheme, } = require("../../loadenv"); -const { userModel } = require("../db/mongo"); +const { userModel } = require("../modules/stores/mongo"); const { user: userPattern } = require("../db/patterns"); const { getLoginInfo, logout, login } = require("../auth/login"); diff --git a/src/services/auth.mobile.js b/src/services/auth.mobile.js index a1940088..f6c68a04 100644 --- a/src/services/auth.mobile.js +++ b/src/services/auth.mobile.js @@ -1,4 +1,4 @@ -const { userModel } = require("../db/mongo"); +const { userModel } = require("../modules/stores/mongo"); const { login } = require("../auth/login"); const { diff --git a/src/services/auth.replace.js b/src/services/auth.replace.js index 3ec2ef28..db2ddf6a 100644 --- a/src/services/auth.replace.js +++ b/src/services/auth.replace.js @@ -1,6 +1,6 @@ const { frontUrl } = require("../../loadenv"); -const { userModel } = require("../db/mongo"); -const { getLoginInfo, logout, login } = require("../auth/login"); +const { userModel } = require("../modules/stores/mongo"); +const { logout, login } = require("../auth/login"); const { registerDeviceToken, diff --git a/src/services/chats.js b/src/services/chats.js index 199dbff2..1763e149 100644 --- a/src/services/chats.js +++ b/src/services/chats.js @@ -1,5 +1,5 @@ -const { chatModel, userModel } = require("../db/mongo"); -const awsS3 = require("../db/awsS3"); +const { chatModel, userModel } = require("../modules/stores/mongo"); +const awsS3 = require("../modules/stores/awsS3"); const { emitChatEvent } = require("./socket.chats"); diff --git a/src/services/locations.js b/src/services/locations.js index 17f95fca..5b02042d 100644 --- a/src/services/locations.js +++ b/src/services/locations.js @@ -1,4 +1,4 @@ -const { locationModel } = require("../db/mongo"); +const { locationModel } = require("../modules/stores/mongo"); const logger = require("../modules/logger"); const getAllLocationsHandler = async (_, res) => { diff --git a/src/services/logininfo.js b/src/services/logininfo.js index 211e7f2e..8ea031b0 100644 --- a/src/services/logininfo.js +++ b/src/services/logininfo.js @@ -1,4 +1,4 @@ -const { userModel } = require("../db/mongo"); +const { userModel } = require("../modules/stores/mongo"); const { getLoginInfo } = require("../auth/login"); const logininfoHandler = (req, res) => { diff --git a/src/services/notifications.js b/src/services/notifications.js index bc568f3a..db533920 100644 --- a/src/services/notifications.js +++ b/src/services/notifications.js @@ -1,4 +1,4 @@ -const { notificationOptionModel } = require("../db/mongo"); +const { notificationOptionModel } = require("../modules/stores/mongo"); const logger = require("../modules/logger"); const optionsHandler = async (req, res) => { diff --git a/src/services/reports.js b/src/services/reports.js index 6cc77191..e2989aee 100644 --- a/src/services/reports.js +++ b/src/services/reports.js @@ -1,4 +1,4 @@ -const { userModel, reportModel } = require("../db/mongo"); +const { userModel, reportModel } = require("../modules/stores/mongo"); const logger = require("../modules/logger"); const reportPopulateOption = [ diff --git a/src/services/rooms.js b/src/services/rooms.js index fb23af8d..cfd3b228 100644 --- a/src/services/rooms.js +++ b/src/services/rooms.js @@ -1,4 +1,8 @@ -const { roomModel, locationModel, userModel } = require("../db/mongo"); +const { + roomModel, + locationModel, + userModel, +} = require("../modules/stores/mongo"); const { emitChatEvent } = require("./socket.chats"); const { leaveChatRoom } = require("../auth/login"); const logger = require("../modules/logger"); diff --git a/src/services/socket.chats.js b/src/services/socket.chats.js index 1fbfc9ed..58e5723a 100644 --- a/src/services/socket.chats.js +++ b/src/services/socket.chats.js @@ -1,6 +1,6 @@ const { getLoginInfo, joinChatRoom, leaveChatRoom } = require("../auth/login"); -const { roomModel, userModel, chatModel } = require("../db/mongo"); -const { getS3Url } = require("../db/awsS3"); +const { roomModel, userModel, chatModel } = require("../modules/stores/mongo"); +const { getS3Url } = require("../modules/stores/awsS3"); const validator = require("validator"); const { getTokensOfUsers, sendMessageByTokens } = require("../modules/fcm"); const logger = require("../modules/logger"); diff --git a/src/services/users.js b/src/services/users.js index d16609ac..31a8403d 100644 --- a/src/services/users.js +++ b/src/services/users.js @@ -1,6 +1,6 @@ -const { userModel, roomModel } = require("../db/mongo"); +const { userModel } = require("../modules/stores/mongo"); const logger = require("../modules/logger"); -const awsS3 = require("../db/awsS3"); +const awsS3 = require("../modules/stores/awsS3"); const agreeOnTermsOfServiceHandler = async (req, res) => { try { @@ -8,9 +8,11 @@ const agreeOnTermsOfServiceHandler = async (req, res) => { if (user.agreeOnTermsOfService !== true) { user.agreeOnTermsOfService = true; await user.save(); - res.status(200).send( - "User/agreeOnTermsOfService : agree on Terms of Service successful" - ); + res + .status(200) + .send( + "User/agreeOnTermsOfService : agree on Terms of Service successful" + ); } else { res.status(400).send("User/agreeOnTermsOfService : already agreed"); } @@ -39,7 +41,9 @@ const editNicknameHandler = async (req, res) => { .findOneAndUpdate({ id: req.userId }, { nickname: newNickname }) .then((result) => { if (result) { - res.status(200).send("User/editNickname : edit user nickname successful"); + res + .status(200) + .send("User/editNickname : edit user nickname successful"); } else { res.status(400).send("User/editNickname : such user id does not exist"); } diff --git a/test/auth.replace.js b/test/auth.replace.js index 913aa906..330d610d 100644 --- a/test/auth.replace.js +++ b/test/auth.replace.js @@ -1,7 +1,7 @@ const request = require("supertest"); const authHandlers = require("../src/services/auth.replace"); -const { userModel } = require("../src/db/mongo"); +const { userModel } = require("../src/modules/stores/mongo"); const { frontUrl } = require("../loadenv"); // auth.replace.js 관련 1개의 handler을 테스트 diff --git a/test/logininfo.js b/test/logininfo.js index 0abee30b..f7605f93 100644 --- a/test/logininfo.js +++ b/test/logininfo.js @@ -1,6 +1,6 @@ const expect = require("chai").expect; const logininfoHandlers = require("../src/services/logininfo"); -const { userModel } = require("../src/db/mongo"); +const { userModel } = require("../src/modules/stores/mongo"); // logininfo.js 관련 2개의 handler을 테스트 (동기식이므로 httpMocks 사용하지 않음) // 1-1. 로그인 한 유저가 없을 시 undefined를 return 하는지 확인 diff --git a/test/reports.js b/test/reports.js index 07c3ebeb..3b86e714 100644 --- a/test/reports.js +++ b/test/reports.js @@ -1,6 +1,6 @@ const expect = require("chai").expect; const reportHandlers = require("../src/services/reports"); -const { userModel } = require("../src/db/mongo"); +const { userModel } = require("../src/modules/stores/mongo"); const { userGenerator, testRemover } = require("./utils"); const httpMocks = require("node-mocks-http"); diff --git a/test/rooms.js b/test/rooms.js index c68248b4..84d916e4 100644 --- a/test/rooms.js +++ b/test/rooms.js @@ -1,7 +1,11 @@ const expect = require("chai").expect; const express = require("express"); const roomsHandlers = require("../src/services/rooms"); -const { userModel, roomModel, locationModel } = require("../src/db/mongo"); +const { + userModel, + roomModel, + locationModel, +} = require("../src/modules/stores/mongo"); const { userGenerator, testRemover } = require("./utils"); const app = express(); const httpMocks = require("node-mocks-http"); diff --git a/test/users.js b/test/users.js index f22285af..1efa0d86 100644 --- a/test/users.js +++ b/test/users.js @@ -1,6 +1,6 @@ const expect = require("chai").expect; const usersHandlers = require("../src/services/users"); -const { userModel } = require("../src/db/mongo"); +const { userModel } = require("../src/modules/stores/mongo"); const { userGenerator, testRemover } = require("./utils"); const httpMocks = require("node-mocks-http"); diff --git a/test/utils.js b/test/utils.js index f2bb6e8e..5a780b72 100644 --- a/test/utils.js +++ b/test/utils.js @@ -4,7 +4,7 @@ const { chatModel, locationModel, reportModel, -} = require("../src/db/mongo"); +} = require("../src/modules/stores/mongo"); const { generateProfileImageUrl } = require("../src/modules/modifyProfile"); // 테스트를 위한 유저 생성 함수 From 99911ebf15105b2db1fa0bfeef43bcc1edaa6fba Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 01:34:05 +0900 Subject: [PATCH 166/308] Move: modules/auths --- src/middlewares/auth.js | 2 +- src/middlewares/authAdmin.js | 2 +- src/modules/{ => auths}/jwt.js | 2 +- src/{auth => modules/auths}/login.js | 2 +- src/{auth/sparcsso.js => modules/auths/sparcssso.js} | 0 src/services/auth.js | 6 +++--- src/services/auth.mobile.js | 4 ++-- src/services/auth.replace.js | 2 +- src/services/logininfo.js | 2 +- src/services/rooms.js | 2 +- src/services/socket.chats.js | 6 +++++- 11 files changed, 17 insertions(+), 13 deletions(-) rename src/modules/{ => auths}/jwt.js (95%) rename src/{auth => modules/auths}/login.js (96%) rename src/{auth/sparcsso.js => modules/auths/sparcssso.js} (100%) diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js index 5654c50b..3e3e7f35 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.js @@ -1,6 +1,6 @@ // 로그인된 상태에만 접근할 수 있는 라우터(rooms)를 위한 미들웨어입니다. -const { isLogin, getLoginInfo } = require("../auth/login"); +const { isLogin, getLoginInfo } = require("../modules/auths/login"); const authMiddleware = (req, res, next) => { if (!isLogin(req)) { diff --git a/src/middlewares/authAdmin.js b/src/middlewares/authAdmin.js index 8919cf96..218f724d 100644 --- a/src/middlewares/authAdmin.js +++ b/src/middlewares/authAdmin.js @@ -1,6 +1,6 @@ // 관리자 유무를 확인하기 위한 미들웨어입니다. -const { isLogin, getLoginInfo } = require("../auth/login"); +const { isLogin, getLoginInfo } = require("../modules/auths/login"); const { frontUrl } = require("../../loadenv"); const { userModel, adminIPWhitelistModel } = require("../modules/stores/mongo"); diff --git a/src/modules/jwt.js b/src/modules/auths/jwt.js similarity index 95% rename from src/modules/jwt.js rename to src/modules/auths/jwt.js index 079b01f0..52945347 100644 --- a/src/modules/jwt.js +++ b/src/modules/auths/jwt.js @@ -1,6 +1,6 @@ const jwt = require("jsonwebtoken"); const { secretKey, option, TOKEN_EXPIRED, TOKEN_INVALID } = - require("../../loadenv").jwt; + require("../../../loadenv").jwt; const signJwt = async ({ id, type }) => { const payload = { diff --git a/src/auth/login.js b/src/modules/auths/login.js similarity index 96% rename from src/auth/login.js rename to src/modules/auths/login.js index c18228a6..3675d2e0 100644 --- a/src/auth/login.js +++ b/src/modules/auths/login.js @@ -1,4 +1,4 @@ -const logger = require("../modules/logger"); +const logger = require("../logger"); const getLoginInfo = (req) => { if (req.session.loginInfo) { diff --git a/src/auth/sparcsso.js b/src/modules/auths/sparcssso.js similarity index 100% rename from src/auth/sparcsso.js rename to src/modules/auths/sparcssso.js diff --git a/src/services/auth.js b/src/services/auth.js index 6d3a1755..d6e27c29 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -6,7 +6,7 @@ const { } = require("../../loadenv"); const { userModel } = require("../modules/stores/mongo"); const { user: userPattern } = require("../db/patterns"); -const { getLoginInfo, logout, login } = require("../auth/login"); +const { getLoginInfo, logout, login } = require("../modules/auths/login"); const { registerDeviceToken, @@ -19,10 +19,10 @@ const { generateProfileImageUrl, getFullUsername, } = require("../modules/modifyProfile"); -const jwt = require("../modules/jwt"); +const jwt = require("../modules/auths/jwt"); // SPARCS SSO -const Client = require("../auth/sparcsso"); +const Client = require("../modules/auths/sparcssso"); const client = new Client(sparcsssoEnv?.id, sparcsssoEnv?.key); const transUserData = (userData) => { diff --git a/src/services/auth.mobile.js b/src/services/auth.mobile.js index f6c68a04..a353eba7 100644 --- a/src/services/auth.mobile.js +++ b/src/services/auth.mobile.js @@ -1,11 +1,11 @@ const { userModel } = require("../modules/stores/mongo"); -const { login } = require("../auth/login"); +const { login } = require("../modules/auths/login"); const { registerDeviceToken, unregisterDeviceToken, } = require("../modules/fcm"); -const jwt = require("../modules/jwt"); +const jwt = require("../modules/auths/jwt"); const logger = require("../modules/logger"); const { TOKEN_EXPIRED, TOKEN_INVALID } = require("../../loadenv").jwt; diff --git a/src/services/auth.replace.js b/src/services/auth.replace.js index db2ddf6a..7a98242a 100644 --- a/src/services/auth.replace.js +++ b/src/services/auth.replace.js @@ -1,6 +1,6 @@ const { frontUrl } = require("../../loadenv"); const { userModel } = require("../modules/stores/mongo"); -const { logout, login } = require("../auth/login"); +const { logout, login } = require("../modules/auths/login"); const { registerDeviceToken, diff --git a/src/services/logininfo.js b/src/services/logininfo.js index 8ea031b0..ddac7b8c 100644 --- a/src/services/logininfo.js +++ b/src/services/logininfo.js @@ -1,5 +1,5 @@ const { userModel } = require("../modules/stores/mongo"); -const { getLoginInfo } = require("../auth/login"); +const { getLoginInfo } = require("../modules/auths/login"); const logininfoHandler = (req, res) => { const user = getLoginInfo(req); diff --git a/src/services/rooms.js b/src/services/rooms.js index cfd3b228..33a18d99 100644 --- a/src/services/rooms.js +++ b/src/services/rooms.js @@ -4,7 +4,7 @@ const { userModel, } = require("../modules/stores/mongo"); const { emitChatEvent } = require("./socket.chats"); -const { leaveChatRoom } = require("../auth/login"); +const { leaveChatRoom } = require("../modules/auths/login"); const logger = require("../modules/logger"); const { roomPopulateOption, diff --git a/src/services/socket.chats.js b/src/services/socket.chats.js index 58e5723a..ed2ce348 100644 --- a/src/services/socket.chats.js +++ b/src/services/socket.chats.js @@ -1,4 +1,8 @@ -const { getLoginInfo, joinChatRoom, leaveChatRoom } = require("../auth/login"); +const { + getLoginInfo, + joinChatRoom, + leaveChatRoom, +} = require("../modules/auths/login"); const { roomModel, userModel, chatModel } = require("../modules/stores/mongo"); const { getS3Url } = require("../modules/stores/awsS3"); const validator = require("validator"); From a877470fb3135d68e45d969db0b5dded8b705e37 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 01:35:43 +0900 Subject: [PATCH 167/308] Move: rooms.js --- src/{db => modules/populates}/rooms.js | 0 src/services/rooms.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{db => modules/populates}/rooms.js (100%) diff --git a/src/db/rooms.js b/src/modules/populates/rooms.js similarity index 100% rename from src/db/rooms.js rename to src/modules/populates/rooms.js diff --git a/src/services/rooms.js b/src/services/rooms.js index 33a18d99..1c08c73b 100644 --- a/src/services/rooms.js +++ b/src/services/rooms.js @@ -10,7 +10,7 @@ const { roomPopulateOption, formatSettlement, getIsOver, -} = require("../db/rooms"); +} = require("../modules/populates/rooms"); const createHandler = async (req, res) => { const { name, from, to, time, maxPartLength } = req.body; From 822df6bce860f8a5951e2f265636814f6182f9be Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 01:37:21 +0900 Subject: [PATCH 168/308] Move: patterns.js --- src/{db => modules}/patterns.js | 0 src/routes/chats.js | 2 +- src/routes/rooms.js | 2 +- src/routes/users.js | 2 +- src/services/auth.js | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/{db => modules}/patterns.js (100%) diff --git a/src/db/patterns.js b/src/modules/patterns.js similarity index 100% rename from src/db/patterns.js rename to src/modules/patterns.js diff --git a/src/routes/chats.js b/src/routes/chats.js index fa8693c6..a4879dd0 100644 --- a/src/routes/chats.js +++ b/src/routes/chats.js @@ -1,7 +1,7 @@ const express = require("express"); const { body } = require("express-validator"); const validator = require("../middlewares/validator"); -const patterns = require("../db/patterns"); +const patterns = require("../modules/patterns"); const router = express.Router(); const chatsHandlers = require("../services/chats"); diff --git a/src/routes/rooms.js b/src/routes/rooms.js index 9eefcf54..b3d4e65d 100644 --- a/src/routes/rooms.js +++ b/src/routes/rooms.js @@ -4,7 +4,7 @@ const router = express.Router(); const roomHandlers = require("../services/rooms"); const validator = require("../middlewares/validator"); -const patterns = require("../db/patterns"); +const patterns = require("../modules/patterns"); // 라우터 접근 시 로그인 필요 router.use(require("../middlewares/auth")); diff --git a/src/routes/users.js b/src/routes/users.js index 556e9954..f7736cd6 100755 --- a/src/routes/users.js +++ b/src/routes/users.js @@ -1,7 +1,7 @@ const express = require("express"); const { body } = require("express-validator"); const validator = require("../middlewares/validator"); -const patterns = require("../db/patterns"); +const patterns = require("../modules/patterns"); const router = express.Router(); const userHandlers = require("../services/users"); diff --git a/src/services/auth.js b/src/services/auth.js index d6e27c29..450658b7 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -5,7 +5,7 @@ const { appUriScheme, } = require("../../loadenv"); const { userModel } = require("../modules/stores/mongo"); -const { user: userPattern } = require("../db/patterns"); +const { user: userPattern } = require("../modules/patterns"); const { getLoginInfo, logout, login } = require("../modules/auths/login"); const { From dde34b66f4386eb2a5879bd6e9de985febc2dff4 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 01:45:18 +0900 Subject: [PATCH 169/308] Add: middlewares/responseTime.js --- app.js | 3 +-- .../logAPIAccess.js => middlewares/responseTime.js} | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) rename src/{modules/logAPIAccess.js => middlewares/responseTime.js} (62%) diff --git a/app.js b/app.js index 744bb7c8..92aee840 100644 --- a/app.js +++ b/app.js @@ -3,7 +3,6 @@ const express = require("express"); const http = require("http"); const { port: httpPort } = require("./loadenv"); const logger = require("./src/modules/logger"); -const logAPIAccess = require("./src/modules/logAPIAccess"); const startSocketServer = require("./src/modules/socket"); // Firebase Admin 초기설정 @@ -28,7 +27,7 @@ app.use(require("cookie-parser")()); app.use(require("./src/middlewares/information")); // [Middleware] API 접근 기록 및 응답 시간을 http response의 헤더에 기록합니다. -app.use(require("response-time")(logAPIAccess)); +app.use(require("./src/middlewares/responseTime")); // [Router] admin 페이지는 rate limiting을 적용하지 않습니다. app.use("/admin", require("./src/routes/admin")); diff --git a/src/modules/logAPIAccess.js b/src/middlewares/responseTime.js similarity index 62% rename from src/modules/logAPIAccess.js rename to src/middlewares/responseTime.js index cc402a34..1d674364 100644 --- a/src/modules/logAPIAccess.js +++ b/src/middlewares/responseTime.js @@ -1,12 +1,11 @@ -const logger = require("./logger"); +const logger = require("../modules/logger"); +const responseTime = require("response-time"); -const logAPIAccess = (req, res, time) => { +module.exports = responseTime((req, res, time) => { const { method, originalUrl, clientIP } = req; const { statusCode } = res; const userId = req.session?.loginInfo?.id || "anonymous"; logger.info( `${userId}(${clientIP}) "${method} ${originalUrl}" ${statusCode} on ${time}ms` ); -}; - -module.exports = logAPIAccess; +}); From 9b0a0b0e8e45352e7069f0631255bc751df93a30 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 22 Mar 2023 01:53:38 +0900 Subject: [PATCH 170/308] Move: populates --- src/modules/populates/chats.js | 10 ++++++++++ src/modules/populates/reports.js | 10 ++++++++++ src/services/reports.js | 7 +------ src/services/socket.chats.js | 8 +------- 4 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 src/modules/populates/chats.js create mode 100644 src/modules/populates/reports.js diff --git a/src/modules/populates/chats.js b/src/modules/populates/chats.js new file mode 100644 index 00000000..2e18ccb6 --- /dev/null +++ b/src/modules/populates/chats.js @@ -0,0 +1,10 @@ +/** @constant {{path: string, select: string}[]} + * 쿼리를 통해 얻은 Chat Document를 populate할 설정값을 정의합니다. + */ +const chatPopulateOption = [ + { path: "authorId", select: "_id nickname profileImageUrl" }, +]; + +module.exports = { + chatPopulateOption, +}; diff --git a/src/modules/populates/reports.js b/src/modules/populates/reports.js new file mode 100644 index 00000000..9f5b3fa4 --- /dev/null +++ b/src/modules/populates/reports.js @@ -0,0 +1,10 @@ +const reportPopulateOption = [ + { + path: "reportedId", + select: "_id id name nickname profileImageUrl", + }, +]; + +module.exports = { + reportPopulateOption, +}; diff --git a/src/services/reports.js b/src/services/reports.js index e2989aee..1ce7b5ae 100644 --- a/src/services/reports.js +++ b/src/services/reports.js @@ -1,12 +1,7 @@ const { userModel, reportModel } = require("../modules/stores/mongo"); +const { reportPopulateOption } = require("../modules/populates/reports"); const logger = require("../modules/logger"); -const reportPopulateOption = [ - { - path: "reportedId", - select: "_id id name nickname profileImageUrl", - }, -]; const createHandler = async (req, res) => { try { const { reportedId, type, etcDetail, time } = req.body; diff --git a/src/services/socket.chats.js b/src/services/socket.chats.js index ed2ce348..717026d9 100644 --- a/src/services/socket.chats.js +++ b/src/services/socket.chats.js @@ -4,18 +4,12 @@ const { leaveChatRoom, } = require("../modules/auths/login"); const { roomModel, userModel, chatModel } = require("../modules/stores/mongo"); +const { chatPopulateOption } = require("../modules/populates/chats"); const { getS3Url } = require("../modules/stores/awsS3"); const validator = require("validator"); const { getTokensOfUsers, sendMessageByTokens } = require("../modules/fcm"); const logger = require("../modules/logger"); -/** @constant {{path: string, select: string}[]} - * 쿼리를 통해 얻은 Chat Document를 populate할 설정값을 정의합니다. - */ -const chatPopulateOption = [ - { path: "authorId", select: "_id nickname profileImageUrl" }, -]; - /** * emitChatEvent의 필수 파라미터가 주어지지 않은 경우 발생하는 예외를 정의하는 클래스입니다. */ From 1e145598442cf1d8fbe6ea975082fadc2af962f1 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 22 Mar 2023 03:40:50 +0900 Subject: [PATCH 171/308] Fix: inject bucket name env variable into s3url --- security.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security.js b/security.js index 9b947f16..2e65b3fd 100644 --- a/security.js +++ b/security.js @@ -18,7 +18,7 @@ module.exports = { s3BucketName: process.env.AWS_S3_BUCKET_NAME, // required s3Url: process.env.AWS_S3_URL || - "https://${process.env.AWS_S3_BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com", // optional + `https://${process.env.AWS_S3_BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com`, // optional }, jwtSecretKey: process.env.JWT_SECRET_KEY, appUriScheme: process.env.APP_URI_SCHEME, From b9da8c5fa14f10f004d79a50f30cd41c1a316a03 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 22 Mar 2023 03:47:11 +0900 Subject: [PATCH 172/308] Add: add fcm option for android --- src/modules/fcm.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index e569392b..4a5af6d8 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -195,6 +195,12 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { link: link || "/", }, }, + android: { + notification: { + icon: icon || "/icons-512.png", + imageUrl: icon || "/icons-512.png", + }, + }, }; const { responses, failureCount } = await getMessaging().sendMulticast( message From a68c6fb339ff9097a3d10466100a23ebbdee9871 Mon Sep 17 00:00:00 2001 From: withsang Date: Wed, 22 Mar 2023 03:50:38 +0900 Subject: [PATCH 173/308] Add: add fcm options for sendMessageByTopic --- src/modules/fcm.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 4a5af6d8..b592d2e2 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -233,6 +233,11 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { try { const message = { topic, + data: { + url: link || "/", + icon: icon || "/icons-512.png", + click_action: "FLUTTER_NOTIFICATION_CLICK", + }, notification: { title, body, @@ -245,6 +250,12 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { link: link || "/", }, }, + android: { + notification: { + icon: icon || "/icons-512.png", + imageUrl: icon || "/icons-512.png", + }, + }, }; await getMessaging().send(message); logger.info(`Notification sent to token ${topic}`); From ffacbe532dadde715145d21fc2aa8ac9492454db Mon Sep 17 00:00:00 2001 From: withsang Date: Fri, 24 Mar 2023 23:22:44 +0900 Subject: [PATCH 174/308] Add: change payload of FCM message --- src/modules/fcm.js | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 887b0a34..a58c5123 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -98,6 +98,7 @@ const removeExpiredTokens = async (deviceTokens, fcmResponses) => { deviceTokens.map(async (deviceToken, index) => { try { // FCM device token이 유효하지 않아 메시지 전송에 실패한 경우, 해당 device token을 DB에서 삭제합니다. + logger.info(fcmResponses[index].error.code); if ( fcmResponses[index].error.code === "messaging/registration-token-not-registered" @@ -107,6 +108,7 @@ const removeExpiredTokens = async (deviceTokens, fcmResponses) => { } return false; } catch (err) { + logger.info(err); return false; } }) @@ -179,22 +181,12 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { const message = { tokens, data: { + title, + body, url: link || "/", icon: icon || "/icons-512.png", click_action: "FLUTTER_NOTIFICATION_CLICK", }, - notification: { - title, - body, - }, - webpush: { - notification: { - icon: icon || "/icons-512.png", - }, - fcm_options: { - link: link || "/", - }, - }, android: { notification: { icon: icon || "/icons-512.png", @@ -234,22 +226,12 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { const message = { topic, data: { + title, + body, url: link || "/", icon: icon || "/icons-512.png", click_action: "FLUTTER_NOTIFICATION_CLICK", }, - notification: { - title, - body, - }, - webpush: { - notification: { - icon: icon || "/icons-512.png", - }, - fcm_options: { - link: link || "/", - }, - }, android: { notification: { icon: icon || "/icons-512.png", From 791d5fa84f494b60e5a2638da385bc4c4cb29ad3 Mon Sep 17 00:00:00 2001 From: withsang Date: Fri, 24 Mar 2023 23:34:11 +0900 Subject: [PATCH 175/308] Fix: remove debugging code --- src/modules/fcm.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index a58c5123..1302c498 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -98,7 +98,6 @@ const removeExpiredTokens = async (deviceTokens, fcmResponses) => { deviceTokens.map(async (deviceToken, index) => { try { // FCM device token이 유효하지 않아 메시지 전송에 실패한 경우, 해당 device token을 DB에서 삭제합니다. - logger.info(fcmResponses[index].error.code); if ( fcmResponses[index].error.code === "messaging/registration-token-not-registered" From 964d0a2461c627916eaaa1b9c5355f0a277a3a63 Mon Sep 17 00:00:00 2001 From: withsang Date: Fri, 24 Mar 2023 23:36:37 +0900 Subject: [PATCH 176/308] Refactor: remove unnecessary android options --- src/modules/fcm.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 1302c498..17dc1cc2 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -186,12 +186,6 @@ const sendMessageByTokens = async (tokens, type, title, body, icon, link) => { icon: icon || "/icons-512.png", click_action: "FLUTTER_NOTIFICATION_CLICK", }, - android: { - notification: { - icon: icon || "/icons-512.png", - imageUrl: icon || "/icons-512.png", - }, - }, }; const { responses, failureCount } = await getMessaging().sendMulticast( message @@ -231,12 +225,6 @@ const sendMessageByTopic = async (topic, type, title, body, icon, link) => { icon: icon || "/icons-512.png", click_action: "FLUTTER_NOTIFICATION_CLICK", }, - android: { - notification: { - icon: icon || "/icons-512.png", - imageUrl: icon || "/icons-512.png", - }, - }, }; await getMessaging().send(message); logger.info(`Notification sent to token ${topic}`); From 9e10e9f35efeaa3b20fb948dd2e3a9e7b13a63df Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 09:25:20 +0000 Subject: [PATCH 177/308] Docs: introduce ajv --- package-lock.json | 194 ++++++++++++++++++++++++++++--- package.json | 3 + src/middlewares/ajv.js | 45 +++++++ src/routes/docs.js | 2 +- src/routes/docs/docs.json | 4 +- src/routes/docs/reportsSchema.js | 27 +++++ src/routes/reports.js | 13 +-- 7 files changed, 259 insertions(+), 29 deletions(-) create mode 100644 src/middlewares/ajv.js create mode 100644 src/routes/docs/reportsSchema.js diff --git a/package-lock.json b/package-lock.json index 84db7a9c..8362709d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,9 @@ "@adminjs/express": "^5.1.0", "@adminjs/mongoose": "^3.0.3", "adminjs": "^6.8.7", + "ajv": "^8.12.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", "aws-sdk": "^2.1182.0", "axios": "^0.27.2", "connect-mongo": "^4.6.0", @@ -3141,6 +3144,21 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -3155,6 +3173,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -4815,13 +4838,13 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { @@ -4829,6 +4852,30 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", + "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", + "peerDependencies": { + "ajv": "^8.0.1" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -6367,6 +6414,21 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -6445,6 +6507,11 @@ "node": ">=8" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8024,9 +8091,9 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -10421,6 +10488,14 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/requizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", @@ -11207,6 +11282,21 @@ "node": ">=6.0.0" } }, + "node_modules/swagger-express-validator/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/swagger-express-validator/node_modules/debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", @@ -11216,6 +11306,11 @@ "ms": "^2.1.1" } }, + "node_modules/swagger-express-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/swagger-express-validator/node_modules/path-to-regexp": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", @@ -14445,6 +14540,17 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -14453,6 +14559,11 @@ "type-fest": "^0.20.2" } }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -15749,16 +15860,30 @@ } }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "requires": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, + "ajv-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", + "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", + "requires": {} + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -16852,6 +16977,17 @@ "text-table": "^0.2.0" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -16900,6 +17036,11 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -18141,9 +18282,9 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -19984,6 +20125,11 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "devOptional": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, "requizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", @@ -20588,6 +20734,17 @@ "validator": "10.11.0" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", @@ -20596,6 +20753,11 @@ "ms": "^2.1.1" } }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "path-to-regexp": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", diff --git a/package.json b/package.json index 8f7466c1..e360ae62 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,9 @@ "@adminjs/express": "^5.1.0", "@adminjs/mongoose": "^3.0.3", "adminjs": "^6.8.7", + "ajv": "^8.12.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", "aws-sdk": "^2.1182.0", "axios": "^0.27.2", "connect-mongo": "^4.6.0", diff --git a/src/middlewares/ajv.js b/src/middlewares/ajv.js new file mode 100644 index 00000000..9d3fbdc9 --- /dev/null +++ b/src/middlewares/ajv.js @@ -0,0 +1,45 @@ +const Ajv = require("ajv"); +const ajvErrors = require("ajv-errors"); +const addFormats = require("ajv-formats").default; + +const ajv = new Ajv({ verbose: true, allErrors: true }); +addFormats(ajv); +ajvErrors(ajv /*, {singleError: true} */); + +const parseAjvErrors = (errors, res) => { + const error_message = errors; + res.status(400).send(error_message); +}; + +const validate = (schema, req, res) => { + const validate = ajv.compile(schema); + if (validate(req)) { + return true; + } else { + parseAjvErrors(validate.errors[0].message, res); + } +}; + +const validateBody = (schema) => { + return (req, res, next) => { + if (validate(schema, req.body, res)) return next(); + }; +}; + +const validateQuery = (schema) => { + return (req, res, next) => { + if (validate(schema, req.query, res)) return next(); + }; +}; + +const validateParams = (schema) => { + return (req, res, next) => { + if (validate(schema, req.params, res)) return next(); + }; +}; + +module.exports = { + validateParams, + validateBody, + validateQuery, +}; diff --git a/src/routes/docs.js b/src/routes/docs.js index da7ebfa2..8d808449 100644 --- a/src/routes/docs.js +++ b/src/routes/docs.js @@ -1,6 +1,6 @@ const express = require("express"); const swaggerUi = require("swagger-ui-express"); -const swaggerSpec = require("../route/docs/docs.json"); +const swaggerSpec = require("./docs/docs.json"); const router = express.Router(); router.use(swaggerUi.serve); diff --git a/src/routes/docs/docs.json b/src/routes/docs/docs.json index a2b4bfb8..c062d58d 100644 --- a/src/routes/docs/docs.json +++ b/src/routes/docs/docs.json @@ -26,7 +26,7 @@ "application/json" ], "paths": { - "/create": { + "/reports/create": { "post": { "tags": [ "reports" @@ -69,7 +69,7 @@ } } }, - "/searchByUser": { + "/reports/searchByUser": { "get": { "tags": [ "reports" diff --git a/src/routes/docs/reportsSchema.js b/src/routes/docs/reportsSchema.js new file mode 100644 index 00000000..5c91bbda --- /dev/null +++ b/src/routes/docs/reportsSchema.js @@ -0,0 +1,27 @@ +const reportsSchema = { + createHandler: { + type: "object", + required: ["reportedId", "type", "time"], + properties: { + reportedId: { + type: "string", + pattern: "^[a-fA-F\\d]{24}$", + }, + type: { + type: "string", + enum: ["no-settlement", "no-show", "etc-reason"], + }, + etcDetail: { + type: "string", + maxLength: 30, + }, + time: { + type: "string", + format: "date-time", + }, + }, + errorMessage: "validation: bad request", + }, +}; + +module.exports = reportsSchema.createHandler; diff --git a/src/routes/reports.js b/src/routes/reports.js index bedc6a93..2468ece1 100644 --- a/src/routes/reports.js +++ b/src/routes/reports.js @@ -1,7 +1,6 @@ const express = require("express"); -const { body } = require("express-validator"); -const validator = require("../middlewares/validator"); - +const reportsSchema = require("./docs/reportsSchema"); +const { validateBody } = require("../middlewares/ajv"); const router = express.Router(); const reportHandlers = require("../services/reports"); @@ -10,13 +9,7 @@ router.use(require("../middlewares/auth")); router.post( "/create", - [ - body("reportedId").isMongoId(), - body("type").isIn(["no-settlement", "no-show", "etc-reason"]), - body("etcDetail").optional().isString().isLength({ max: 30 }), - body("time").isISO8601(), - ], - validator, + validateBody(reportsSchema), reportHandlers.createHandler ); From 402e3f166ffb798fbf563fbf0928a31728e3cc7c Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 09:40:14 +0000 Subject: [PATCH 178/308] Refactor: change json to js --- src/middlewares/ajv.js | 2 +- src/routes/docs.js | 4 +- src/routes/docs/docs.json | 323 --------------------------------- src/routes/docs/swaggerDocs.js | 286 +++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+), 326 deletions(-) delete mode 100644 src/routes/docs/docs.json create mode 100644 src/routes/docs/swaggerDocs.js diff --git a/src/middlewares/ajv.js b/src/middlewares/ajv.js index 9d3fbdc9..05ab9f8c 100644 --- a/src/middlewares/ajv.js +++ b/src/middlewares/ajv.js @@ -4,7 +4,7 @@ const addFormats = require("ajv-formats").default; const ajv = new Ajv({ verbose: true, allErrors: true }); addFormats(ajv); -ajvErrors(ajv /*, {singleError: true} */); +ajvErrors(ajv); const parseAjvErrors = (errors, res) => { const error_message = errors; diff --git a/src/routes/docs.js b/src/routes/docs.js index 8d808449..97a1b288 100644 --- a/src/routes/docs.js +++ b/src/routes/docs.js @@ -1,9 +1,9 @@ const express = require("express"); const swaggerUi = require("swagger-ui-express"); -const swaggerSpec = require("./docs/docs.json"); +const swaggerDocs = require("./docs/swaggerDocs.js"); const router = express.Router(); router.use(swaggerUi.serve); -router.use(swaggerUi.setup(swaggerSpec, { explorer: true })); +router.use(swaggerUi.setup(swaggerDocs, { explorer: true })); module.exports = router; diff --git a/src/routes/docs/docs.json b/src/routes/docs/docs.json deleted file mode 100644 index c062d58d..00000000 --- a/src/routes/docs/docs.json +++ /dev/null @@ -1,323 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Taxi API Document", - "version": "1.0.0" - }, - "basePath": "/", - "tags": [ - { - "name": "locations", - "description": "출발지/도착지 정보 제공" - }, - { - "name": "logininfo", - "description": "로그인 정보 제공" - }, - { - "name": "reports", - "description": "사용자 신고 및 신고 기록 조회" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/reports/create": { - "post": { - "tags": [ - "reports" - ], - "summary": "신고 작성", - "description": "주어진 유저를 전달된 사유로 신고함", - "requestBody": { - "description": "Update an existent user in the store", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/createHandler" - } - } - } - }, - "responses": { - "200": { - "description": "report successful", - "content": { - "text/plain": { - "schema": { - "type": "string", - "example": "report successful" - } - } - } - }, - "500": { - "description": "internal server error", - "content": { - "text/plain": { - "schema": { - "type": "string", - "example": "internal server error" - } - } - } - } - } - } - }, - "/reports/searchByUser": { - "get": { - "tags": [ - "reports" - ], - "summary": "신고 내역 반환", - "description": "로그인된 사용자의 신고한 내역과, 신고받은 내역을 반환한다
1000개의 limit이 있다.", - "responses": { - "200": { - "description": "신고된 내역과 신고 받은 내역", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "reporting": { - "type": "array" - }, - "reported": { - "type": "array" - } - } - } - } - } - }, - "500": { - "description": "internal server error", - "content": { - "text/plain": { - "schema": { - "type": "string", - "example": "internal server error" - } - } - } - } - } - } - }, - "/logininfo/detail": { - "get": { - "tags": [ - "logininfo" - ], - "summary": "상세한 사용자 정보 반환", - "description": "로그인되어 있는 사용자의 상세한 정보를 반환", - "responses": { - "200": { - "description": "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
\n 세션이 유효하지 않은 경우, 빈 오브젝트를 반환", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "oid": { - "type": "string" - }, - "id": { - "type": "string", - "description": "사용자 id" - }, - "name": { - "type": "string", - "description": "사용자 이름" - }, - "nickname": { - "type": "string" - }, - "withdraw": { - "type": "boolean" - }, - "ban": { - "type": "boolean" - }, - "joinat": { - "type": "string", - "format": "date-time" - }, - "agreeOnTermsOfService": { - "type": "boolean" - }, - "subinfio": { - "type": "object", - "properties": { - "kaist": { - "type": "string", - "description": "KAIST 학번(8자리)", - "minLength": 8, - "maxLength": 8, - "example": "20190052" - }, - "sparcs": { - "type": "string" - }, - "facebook": { - "type": "string" - }, - "twitter": { - "type": "string" - } - } - }, - "email": { - "type": "string", - "example": "geon6757@kaist.ac.kr" - }, - "profileImgUrl": { - "type": "string" - }, - "account": { - "type": "string" - } - } - } - } - } - } - } - } - }, - "/logininfo": { - "get": { - "tags": [ - "logininfo" - ], - "summary": "사용자 정보 반환", - "description": "로그인되어 있는 사용자의 정보를 반환", - "responses": { - "200": { - "description": "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
세션이 유효하지 않은 경우, 빈 오브젝트를 반환", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "사용자 id" - }, - "sid": { - "type": "string", - "description": "사용자 sid" - }, - "name": { - "type": "string", - "description": "사용자 이름" - } - } - } - } - } - } - } - } - }, - "/locations": { - "get": { - "tags": [ - "locations" - ], - "summary": "출발지/도착지 정보 반환", - "description": "출발지/도착지로 사용 가능한 장소 목록 조회 및 요청 처리 당시 서버 시각 반환
\n (로그인된 상태에서만 접근 가능)", - "responses": { - "200": { - "description": "서버에 저장된 location이 없을 경우, locations은 빈 배열", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "locations": { - "type": "array", - "description": "출발지/도착지로 사용 가능한 장소 목록", - "items": { - "type": "object", - "properties": { - "priority": { - "type": "number" - }, - "isValid": { - "type": "boolean" - }, - "_id": { - "type": "string" - }, - "koName": { - "type": "string", - "description": "장소의 한국어 명칭", - "example": "택시승강장" - }, - "enName": { - "type": "string", - "description": "장소의 영어 명칭", - "example": "Taxi Stand" - } - } - } - }, - "serverTime": { - "type": "string", - "format": "date-time", - "description": "요청 처리 당시 서버 시각" - } - } - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "createHandler": { - "type": "object", - "required": [ - "reportedId", - "type", - "time" - ], - "properties": { - "reportedId": { - "type": "string", - "pattern": "^[a-fA-F\\d]{24}$" - }, - "type": { - "type": "string", - "enum": [ - "no-settlement", - "no-show", - "etc-reason" - ] - }, - "etcDetail": { - "type": "string", - "maxLength": 30 - }, - "time": { - "type": "string", - "format": "date-time" - } - } - } - } - }, - "apis": [ - "src/route/*.js", - "src/route/docs/*.json" - ] -} \ No newline at end of file diff --git a/src/routes/docs/swaggerDocs.js b/src/routes/docs/swaggerDocs.js new file mode 100644 index 00000000..3d2c1a7b --- /dev/null +++ b/src/routes/docs/swaggerDocs.js @@ -0,0 +1,286 @@ +const reportsSchema = require("./reportsSchema"); + +const swaggerDocs = { + openapi: "3.0.3", + info: { + title: "Taxi API Document", + version: "1.0.0", + }, + basePath: "/", + tags: [ + { + name: "locations", + description: "출발지/도착지 정보 제공", + }, + { + name: "logininfo", + description: "로그인 정보 제공", + }, + { + name: "reports", + description: "사용자 신고 및 신고 기록 조회", + }, + ], + consumes: ["application/json"], + produces: ["application/json"], + paths: { + "/reports/create": { + post: { + tags: ["reports"], + summary: "신고 작성", + description: "주어진 유저를 전달된 사유로 신고함", + requestBody: { + description: "Update an existent user in the store", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/createHandler", + }, + }, + }, + }, + responses: { + 200: { + description: "report successful", + content: { + "text/plain": { + schema: { + type: "string", + example: "report successful", + }, + }, + }, + }, + 500: { + description: "internal server error", + content: { + "text/plain": { + schema: { + type: "string", + example: "internal server error", + }, + }, + }, + }, + }, + }, + }, + "/reports/searchByUser": { + get: { + tags: ["reports"], + summary: "신고 내역 반환", + description: + "로그인된 사용자의 신고한 내역과, 신고받은 내역을 반환한다
1000개의 limit이 있다.", + responses: { + 200: { + description: "신고된 내역과 신고 받은 내역", + content: { + "application/json": { + schema: { + type: "object", + properties: { + reporting: { + type: "array", + }, + reported: { + type: "array", + }, + }, + }, + }, + }, + }, + 500: { + description: "internal server error", + content: { + "text/plain": { + schema: { + type: "string", + example: "internal server error", + }, + }, + }, + }, + }, + }, + }, + "/logininfo/detail": { + get: { + tags: ["logininfo"], + summary: "상세한 사용자 정보 반환", + description: "로그인되어 있는 사용자의 상세한 정보를 반환", + responses: { + 200: { + description: + "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
\n 세션이 유효하지 않은 경우, 빈 오브젝트를 반환", + content: { + "application/json": { + schema: { + type: "object", + properties: { + oid: { + type: "string", + }, + id: { + type: "string", + description: "사용자 id", + }, + name: { + type: "string", + description: "사용자 이름", + }, + nickname: { + type: "string", + }, + withdraw: { + type: "boolean", + }, + ban: { + type: "boolean", + }, + joinat: { + type: "string", + format: "date-time", + }, + agreeOnTermsOfService: { + type: "boolean", + }, + subinfio: { + type: "object", + properties: { + kaist: { + type: "string", + description: "KAIST 학번(8자리)", + minLength: 8, + maxLength: 8, + example: "20190052", + }, + sparcs: { + type: "string", + }, + facebook: { + type: "string", + }, + twitter: { + type: "string", + }, + }, + }, + email: { + type: "string", + example: "geon6757@kaist.ac.kr", + }, + profileImgUrl: { + type: "string", + }, + account: { + type: "string", + }, + }, + }, + }, + }, + }, + }, + }, + }, + "/logininfo": { + get: { + tags: ["logininfo"], + summary: "사용자 정보 반환", + description: "로그인되어 있는 사용자의 정보를 반환", + responses: { + 200: { + description: + "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
세션이 유효하지 않은 경우, 빈 오브젝트를 반환", + content: { + "application/json": { + schema: { + type: "object", + properties: { + id: { + type: "string", + description: "사용자 id", + }, + sid: { + type: "string", + description: "사용자 sid", + }, + name: { + type: "string", + description: "사용자 이름", + }, + }, + }, + }, + }, + }, + }, + }, + }, + "/locations": { + get: { + tags: ["locations"], + summary: "출발지/도착지 정보 반환", + description: + "출발지/도착지로 사용 가능한 장소 목록 조회 및 요청 처리 당시 서버 시각 반환
\n (로그인된 상태에서만 접근 가능)", + responses: { + 200: { + description: + "서버에 저장된 location이 없을 경우, locations은 빈 배열", + content: { + "application/json": { + schema: { + type: "object", + properties: { + locations: { + type: "array", + description: "출발지/도착지로 사용 가능한 장소 목록", + items: { + type: "object", + properties: { + priority: { + type: "number", + }, + isValid: { + type: "boolean", + }, + _id: { + type: "string", + }, + koName: { + type: "string", + description: "장소의 한국어 명칭", + example: "택시승강장", + }, + enName: { + type: "string", + description: "장소의 영어 명칭", + example: "Taxi Stand", + }, + }, + }, + }, + serverTime: { + type: "string", + format: "date-time", + description: "요청 처리 당시 서버 시각", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + createHandler: reportsSchema, + }, + }, + apis: ["src/route/*.js", "src/route/docs/*.json"], +}; + +module.exports = swaggerDocs; From 0fd28eb326edf1d3507ca802d018d40e21a300cf Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 09:51:27 +0000 Subject: [PATCH 179/308] Refactor: split swagger docs --- src/routes/docs/locations.js | 60 +++++++ src/routes/docs/logininfo.js | 118 ++++++++++++++ src/routes/docs/reports.js | 84 ++++++++++ src/routes/docs/reportsSchema.js | 2 +- src/routes/docs/swaggerDocs.js | 260 ++----------------------------- src/routes/reports.js | 2 +- 6 files changed, 273 insertions(+), 253 deletions(-) create mode 100644 src/routes/docs/locations.js create mode 100644 src/routes/docs/logininfo.js create mode 100644 src/routes/docs/reports.js diff --git a/src/routes/docs/locations.js b/src/routes/docs/locations.js new file mode 100644 index 00000000..530788eb --- /dev/null +++ b/src/routes/docs/locations.js @@ -0,0 +1,60 @@ +const locationsDocs = { + "/locations": { + get: { + tags: ["locations"], + summary: "출발지/도착지 정보 반환", + description: + "출발지/도착지로 사용 가능한 장소 목록 조회 및 요청 처리 당시 서버 시각 반환
\n (로그인된 상태에서만 접근 가능)", + responses: { + 200: { + description: + "서버에 저장된 location이 없을 경우, locations은 빈 배열", + content: { + "application/json": { + schema: { + type: "object", + properties: { + locations: { + type: "array", + description: "출발지/도착지로 사용 가능한 장소 목록", + items: { + type: "object", + properties: { + priority: { + type: "number", + }, + isValid: { + type: "boolean", + }, + _id: { + type: "string", + }, + koName: { + type: "string", + description: "장소의 한국어 명칭", + example: "택시승강장", + }, + enName: { + type: "string", + description: "장소의 영어 명칭", + example: "Taxi Stand", + }, + }, + }, + }, + serverTime: { + type: "string", + format: "date-time", + description: "요청 처리 당시 서버 시각", + }, + }, + }, + }, + }, + }, + }, + }, + }, +}; + +module.exports = locationsDocs; diff --git a/src/routes/docs/logininfo.js b/src/routes/docs/logininfo.js new file mode 100644 index 00000000..9756484d --- /dev/null +++ b/src/routes/docs/logininfo.js @@ -0,0 +1,118 @@ +const logininfoDocs = { + "/logininfo/detail": { + get: { + tags: ["logininfo"], + summary: "상세한 사용자 정보 반환", + description: "로그인되어 있는 사용자의 상세한 정보를 반환", + responses: { + 200: { + description: + "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
\n 세션이 유효하지 않은 경우, 빈 오브젝트를 반환", + content: { + "application/json": { + schema: { + type: "object", + properties: { + oid: { + type: "string", + }, + id: { + type: "string", + description: "사용자 id", + }, + name: { + type: "string", + description: "사용자 이름", + }, + nickname: { + type: "string", + }, + withdraw: { + type: "boolean", + }, + ban: { + type: "boolean", + }, + joinat: { + type: "string", + format: "date-time", + }, + agreeOnTermsOfService: { + type: "boolean", + }, + subinfio: { + type: "object", + properties: { + kaist: { + type: "string", + description: "KAIST 학번(8자리)", + minLength: 8, + maxLength: 8, + example: "20190052", + }, + sparcs: { + type: "string", + }, + facebook: { + type: "string", + }, + twitter: { + type: "string", + }, + }, + }, + email: { + type: "string", + example: "geon6757@kaist.ac.kr", + }, + profileImgUrl: { + type: "string", + }, + account: { + type: "string", + }, + }, + }, + }, + }, + }, + }, + }, + }, + "/logininfo": { + get: { + tags: ["logininfo"], + summary: "사용자 정보 반환", + description: "로그인되어 있는 사용자의 정보를 반환", + responses: { + 200: { + description: + "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
세션이 유효하지 않은 경우, 빈 오브젝트를 반환", + content: { + "application/json": { + schema: { + type: "object", + properties: { + id: { + type: "string", + description: "사용자 id", + }, + sid: { + type: "string", + description: "사용자 sid", + }, + name: { + type: "string", + description: "사용자 이름", + }, + }, + }, + }, + }, + }, + }, + }, + }, +}; + +module.exports = logininfoDocs; diff --git a/src/routes/docs/reports.js b/src/routes/docs/reports.js new file mode 100644 index 00000000..0210197e --- /dev/null +++ b/src/routes/docs/reports.js @@ -0,0 +1,84 @@ +const reportsDocs = { + "/reports/create": { + post: { + tags: ["reports"], + summary: "신고 작성", + description: "주어진 유저를 전달된 사유로 신고함", + requestBody: { + description: "Update an existent user in the store", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/createHandler", + }, + }, + }, + }, + responses: { + 200: { + description: "report successful", + content: { + "text/plain": { + schema: { + type: "string", + example: "report successful", + }, + }, + }, + }, + 500: { + description: "internal server error", + content: { + "text/plain": { + schema: { + type: "string", + example: "internal server error", + }, + }, + }, + }, + }, + }, + }, + "/reports/searchByUser": { + get: { + tags: ["reports"], + summary: "신고 내역 반환", + description: + "로그인된 사용자의 신고한 내역과, 신고받은 내역을 반환한다
1000개의 limit이 있다.", + responses: { + 200: { + description: "신고된 내역과 신고 받은 내역", + content: { + "application/json": { + schema: { + type: "object", + properties: { + reporting: { + type: "array", + }, + reported: { + type: "array", + }, + }, + }, + }, + }, + }, + 500: { + description: "internal server error", + content: { + "text/plain": { + schema: { + type: "string", + example: "internal server error", + }, + }, + }, + }, + }, + }, + }, +}; + +module.exports = reportsDocs; diff --git a/src/routes/docs/reportsSchema.js b/src/routes/docs/reportsSchema.js index 5c91bbda..80cc21c1 100644 --- a/src/routes/docs/reportsSchema.js +++ b/src/routes/docs/reportsSchema.js @@ -24,4 +24,4 @@ const reportsSchema = { }, }; -module.exports = reportsSchema.createHandler; +module.exports = reportsSchema; diff --git a/src/routes/docs/swaggerDocs.js b/src/routes/docs/swaggerDocs.js index 3d2c1a7b..88ed5929 100644 --- a/src/routes/docs/swaggerDocs.js +++ b/src/routes/docs/swaggerDocs.js @@ -1,4 +1,7 @@ const reportsSchema = require("./reportsSchema"); +const reportDocs = require("./reports"); +const logininfoDocs = require("./logininfo"); +const locationsDocs = require("./locations"); const swaggerDocs = { openapi: "3.0.3", @@ -24,260 +27,15 @@ const swaggerDocs = { consumes: ["application/json"], produces: ["application/json"], paths: { - "/reports/create": { - post: { - tags: ["reports"], - summary: "신고 작성", - description: "주어진 유저를 전달된 사유로 신고함", - requestBody: { - description: "Update an existent user in the store", - content: { - "application/json": { - schema: { - $ref: "#/components/schemas/createHandler", - }, - }, - }, - }, - responses: { - 200: { - description: "report successful", - content: { - "text/plain": { - schema: { - type: "string", - example: "report successful", - }, - }, - }, - }, - 500: { - description: "internal server error", - content: { - "text/plain": { - schema: { - type: "string", - example: "internal server error", - }, - }, - }, - }, - }, - }, - }, - "/reports/searchByUser": { - get: { - tags: ["reports"], - summary: "신고 내역 반환", - description: - "로그인된 사용자의 신고한 내역과, 신고받은 내역을 반환한다
1000개의 limit이 있다.", - responses: { - 200: { - description: "신고된 내역과 신고 받은 내역", - content: { - "application/json": { - schema: { - type: "object", - properties: { - reporting: { - type: "array", - }, - reported: { - type: "array", - }, - }, - }, - }, - }, - }, - 500: { - description: "internal server error", - content: { - "text/plain": { - schema: { - type: "string", - example: "internal server error", - }, - }, - }, - }, - }, - }, - }, - "/logininfo/detail": { - get: { - tags: ["logininfo"], - summary: "상세한 사용자 정보 반환", - description: "로그인되어 있는 사용자의 상세한 정보를 반환", - responses: { - 200: { - description: - "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
\n 세션이 유효하지 않은 경우, 빈 오브젝트를 반환", - content: { - "application/json": { - schema: { - type: "object", - properties: { - oid: { - type: "string", - }, - id: { - type: "string", - description: "사용자 id", - }, - name: { - type: "string", - description: "사용자 이름", - }, - nickname: { - type: "string", - }, - withdraw: { - type: "boolean", - }, - ban: { - type: "boolean", - }, - joinat: { - type: "string", - format: "date-time", - }, - agreeOnTermsOfService: { - type: "boolean", - }, - subinfio: { - type: "object", - properties: { - kaist: { - type: "string", - description: "KAIST 학번(8자리)", - minLength: 8, - maxLength: 8, - example: "20190052", - }, - sparcs: { - type: "string", - }, - facebook: { - type: "string", - }, - twitter: { - type: "string", - }, - }, - }, - email: { - type: "string", - example: "geon6757@kaist.ac.kr", - }, - profileImgUrl: { - type: "string", - }, - account: { - type: "string", - }, - }, - }, - }, - }, - }, - }, - }, - }, - "/logininfo": { - get: { - tags: ["logininfo"], - summary: "사용자 정보 반환", - description: "로그인되어 있는 사용자의 정보를 반환", - responses: { - 200: { - description: - "사용자의 로그인 세션이 유효한 경우, 현재 로그인된 사용자의 정보를 반환,
세션이 유효하지 않은 경우, 빈 오브젝트를 반환", - content: { - "application/json": { - schema: { - type: "object", - properties: { - id: { - type: "string", - description: "사용자 id", - }, - sid: { - type: "string", - description: "사용자 sid", - }, - name: { - type: "string", - description: "사용자 이름", - }, - }, - }, - }, - }, - }, - }, - }, - }, - "/locations": { - get: { - tags: ["locations"], - summary: "출발지/도착지 정보 반환", - description: - "출발지/도착지로 사용 가능한 장소 목록 조회 및 요청 처리 당시 서버 시각 반환
\n (로그인된 상태에서만 접근 가능)", - responses: { - 200: { - description: - "서버에 저장된 location이 없을 경우, locations은 빈 배열", - content: { - "application/json": { - schema: { - type: "object", - properties: { - locations: { - type: "array", - description: "출발지/도착지로 사용 가능한 장소 목록", - items: { - type: "object", - properties: { - priority: { - type: "number", - }, - isValid: { - type: "boolean", - }, - _id: { - type: "string", - }, - koName: { - type: "string", - description: "장소의 한국어 명칭", - example: "택시승강장", - }, - enName: { - type: "string", - description: "장소의 영어 명칭", - example: "Taxi Stand", - }, - }, - }, - }, - serverTime: { - type: "string", - format: "date-time", - description: "요청 처리 당시 서버 시각", - }, - }, - }, - }, - }, - }, - }, - }, - }, + "/reports/create": reportDocs["/reports/create"], + "/reports/searchByUser": reportDocs["/reports/searchByUser"], + "/logininfo": logininfoDocs["/logininfo"], + "/logininfo/detail": logininfoDocs["/logininfo/detail"], + "/locations": locationsDocs["/locations"], }, components: { schemas: { - createHandler: reportsSchema, + createHandler: reportsSchema.createHandler, }, }, apis: ["src/route/*.js", "src/route/docs/*.json"], diff --git a/src/routes/reports.js b/src/routes/reports.js index 2468ece1..07fcbf51 100644 --- a/src/routes/reports.js +++ b/src/routes/reports.js @@ -9,7 +9,7 @@ router.use(require("../middlewares/auth")); router.post( "/create", - validateBody(reportsSchema), + validateBody(reportsSchema.createHandler), reportHandlers.createHandler ); From b53a7be8f18716ffa6ff62ae23f1e6911489ca03 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 11:59:34 +0000 Subject: [PATCH 180/308] Remove: unused packages --- package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index e360ae62..fe8eba1c 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,6 @@ "redis": "^4.2.0", "response-time": "^2.3.2", "socket.io": "^4.3.1", - "swagger-express-validator": "^1.0.2", - "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^4.6.0", "validator": "^13.7.0", "winston": "^3.8.1", @@ -58,4 +56,4 @@ }, "author": "sparcs/taxi", "license": "MIT" -} +} \ No newline at end of file From ba4a929eafa70c94e8559e70251e28095cb7d42a Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 13:37:58 +0000 Subject: [PATCH 181/308] Add: disable login restriction --- loadenv.js | 1 + src/services/auth.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/loadenv.js b/loadenv.js index 8ff234c3..8eae4ef9 100644 --- a/loadenv.js +++ b/loadenv.js @@ -33,4 +33,5 @@ module.exports = { googleApplicationCredentials: process.env.GOOGLE_APPLICATION_CREDENTIALS && JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS), + testAccounts: process.env.TEST_ACCOUNTS.split(","), }; diff --git a/src/services/auth.js b/src/services/auth.js index 450658b7..05918ee6 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -3,6 +3,7 @@ const { frontUrl, nodeEnv, appUriScheme, + testAccounts, } = require("../../loadenv"); const { userModel } = require("../modules/stores/mongo"); const { user: userPattern } = require("../modules/patterns"); @@ -115,7 +116,8 @@ const sparcsssoCallbackHandler = (req, res) => { const code = req.body.code || req.query.code; client.getUserInfo(code).then((userDataBefore) => { const userData = transUserData(userDataBefore); - if (userData.isEligible || nodeEnv !== "production") { + const isTestAccount = testAccounts.includes(userData.email); + if (userData.isEligible || nodeEnv !== "production" || isTestAccount) { if (req.session.isApp) { createNewTokenHandler(req, res, userData); } else { From 84049806cdd9e9d46e9214428c8eaec68a056bd2 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 13:43:04 +0000 Subject: [PATCH 182/308] Refactor: use JSON.parse --- loadenv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loadenv.js b/loadenv.js index 8eae4ef9..eb0e46a4 100644 --- a/loadenv.js +++ b/loadenv.js @@ -33,5 +33,5 @@ module.exports = { googleApplicationCredentials: process.env.GOOGLE_APPLICATION_CREDENTIALS && JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS), - testAccounts: process.env.TEST_ACCOUNTS.split(","), + testAccounts: JSON.parse(process.env.TEST_ACCOUNTS), }; From 2e8bd20f96461bbb058467b861836669ff3c34f1 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 13:48:31 +0000 Subject: [PATCH 183/308] Fix: add default value --- loadenv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loadenv.js b/loadenv.js index eb0e46a4..3d5cbb47 100644 --- a/loadenv.js +++ b/loadenv.js @@ -33,5 +33,5 @@ module.exports = { googleApplicationCredentials: process.env.GOOGLE_APPLICATION_CREDENTIALS && JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS), - testAccounts: JSON.parse(process.env.TEST_ACCOUNTS), + testAccounts: JSON.parse(process.env.TEST_ACCOUNTS) || [], }; From 7978828b9b527be42b266fe0e941b0c1a9512be2 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 13:53:53 +0000 Subject: [PATCH 184/308] Fix: loadenv.js --- loadenv.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/loadenv.js b/loadenv.js index 3d5cbb47..6c1e887e 100644 --- a/loadenv.js +++ b/loadenv.js @@ -33,5 +33,6 @@ module.exports = { googleApplicationCredentials: process.env.GOOGLE_APPLICATION_CREDENTIALS && JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS), - testAccounts: JSON.parse(process.env.TEST_ACCOUNTS) || [], + testAccounts: + process.env.testAccounts && JSON.parse(process.env.TEST_ACCOUNTS), }; From 707ee28771cfa7b25e391cc51c61dd02b4e1a62b Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 14:03:15 +0000 Subject: [PATCH 185/308] Refactor: swaggerDocs.js --- src/routes/docs/swaggerDocs.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/routes/docs/swaggerDocs.js b/src/routes/docs/swaggerDocs.js index 88ed5929..8100a038 100644 --- a/src/routes/docs/swaggerDocs.js +++ b/src/routes/docs/swaggerDocs.js @@ -27,18 +27,15 @@ const swaggerDocs = { consumes: ["application/json"], produces: ["application/json"], paths: { - "/reports/create": reportDocs["/reports/create"], - "/reports/searchByUser": reportDocs["/reports/searchByUser"], - "/logininfo": logininfoDocs["/logininfo"], - "/logininfo/detail": logininfoDocs["/logininfo/detail"], - "/locations": locationsDocs["/locations"], + ...reportDocs, + ...logininfoDocs, + ...locationsDocs, }, components: { schemas: { - createHandler: reportsSchema.createHandler, + ...reportsSchema, }, }, - apis: ["src/route/*.js", "src/route/docs/*.json"], }; module.exports = swaggerDocs; From 17ed98a9440c925555e0ff376496245a3c093801 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 14:03:59 +0000 Subject: [PATCH 186/308] Remove: reports.md --- src/routes/docs/reports.md | 75 -------------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 src/routes/docs/reports.md diff --git a/src/routes/docs/reports.md b/src/routes/docs/reports.md deleted file mode 100644 index 55103208..00000000 --- a/src/routes/docs/reports.md +++ /dev/null @@ -1,75 +0,0 @@ -## `/reports` - -- 사용자 신고 및 신고 기록 조회 기능을 지원하는 API. -- 로그인된 상태에서만 접근 가능 -- report를 반환할 경우 그 type은 다음과 같다. - -```javascript -Report { - creatorId: { - _id: ObjectId, // 신고한 사용자 Document의 ObjectId - id: String, // 신고한 사용자 id - name: String, // 신고한 사용자 이름 - nickname: String, // 신고한 사용자 닉네임 - profileImageUrl: String, // 프로필 사진 url - }, - reportedId: { - _id: ObjectId, // 신고당한 사용자 Document의 ObjectId - id: String, // 신고당한 사용자 id - name: String, // 신고당한 사용자 이름 - nickname: String, // 신고당한 사용자 닉네임 - profileImageUrl: String, // 프로필 사진 url - }, - type: String, // 신고 사유 - etcDetail: String, // 기타 세부 사유 - time: Date, // 신고 날짜 -} -``` - -`type` 속성은 아래 세 가지 값들 중 하나를 가진다. -1. `"no-settlement"` : 정산을 하지 않아서 신고 -2. `"no-show"` : 방 참여 후 약속 장소에 오지 않아서 신고 -3. `"etc-reason"` : 기타 사유로 신고 - -### `/create` **(POST)** - -- 신고 작성함 - -#### URL Parameters, Request JSON form - -- reportedId : 신고할 user의 ID -- type : 신고 사유 유형 -- etcDetail: 기타 신고 사유 내용 -- time : 신고 날짜 - -#### Response - -- 200 "report successful" - -#### Errors - -- 500 "internal server error" - - - -### `/searchByUser` **(GET)** - -- 로그인된 사용자가 신고한 내역과, 신고받은 내역을 반환한다. -- 1000개의 limit이 있다. - -#### URL Parameters, Request JSON form - -없음 - -#### Response - -```javascript -{ - reporting: [Report], // 신고한 내역 - reported: [Report], // 신고받은 내역 -} -``` - -#### Errors - -- 500 "internal server error" From 9b3c740a7a8bc0f7d32b8b1ca2ca07056d698884 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 14:06:52 +0000 Subject: [PATCH 187/308] Refactor: swaggerDocs --- src/routes/docs/swaggerDocs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/docs/swaggerDocs.js b/src/routes/docs/swaggerDocs.js index 8100a038..cf52ff2a 100644 --- a/src/routes/docs/swaggerDocs.js +++ b/src/routes/docs/swaggerDocs.js @@ -1,5 +1,5 @@ const reportsSchema = require("./reportsSchema"); -const reportDocs = require("./reports"); +const reportsDocs = require("./reports"); const logininfoDocs = require("./logininfo"); const locationsDocs = require("./locations"); @@ -27,7 +27,7 @@ const swaggerDocs = { consumes: ["application/json"], produces: ["application/json"], paths: { - ...reportDocs, + ...reportsDocs, ...logininfoDocs, ...locationsDocs, }, From f54625124ddf8879b8c796019446dcd1654d5b5a Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 28 Mar 2023 14:18:09 +0000 Subject: [PATCH 188/308] Fix: handle undefined case --- src/services/auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/auth.js b/src/services/auth.js index 05918ee6..ec588be9 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -116,7 +116,7 @@ const sparcsssoCallbackHandler = (req, res) => { const code = req.body.code || req.query.code; client.getUserInfo(code).then((userDataBefore) => { const userData = transUserData(userDataBefore); - const isTestAccount = testAccounts.includes(userData.email); + const isTestAccount = testAccounts?.includes(userData.email); if (userData.isEligible || nodeEnv !== "production" || isTestAccount) { if (req.session.isApp) { createNewTokenHandler(req, res, userData); From 19503f1cf5690ad3bbda334f84a99c6def400295 Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 29 Mar 2023 06:44:45 +0000 Subject: [PATCH 189/308] Refactor: ajv.js --- src/middlewares/ajv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewares/ajv.js b/src/middlewares/ajv.js index 05ab9f8c..0c8a8a3b 100644 --- a/src/middlewares/ajv.js +++ b/src/middlewares/ajv.js @@ -1,6 +1,6 @@ const Ajv = require("ajv"); const ajvErrors = require("ajv-errors"); -const addFormats = require("ajv-formats").default; +const { default: addFormats } = require("ajv-formats"); const ajv = new Ajv({ verbose: true, allErrors: true }); addFormats(ajv); From 5b72894e31307d8e64067abcf965ee1cdc513138 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Fri, 31 Mar 2023 21:09:39 +0900 Subject: [PATCH 190/308] Move: auth/registerDeviceToken to notifications/registerDeviceToken --- package-lock.json | 411 ---------------------------------- package.json | 2 +- src/routes/auth.js | 8 - src/routes/auth.replace.js | 9 - src/routes/notifications.js | 10 + src/services/auth.js | 35 +-- src/services/auth.replace.js | 1 + src/services/notifications.js | 47 +++- 8 files changed, 57 insertions(+), 466 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8362709d..e3b0c539 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,8 +38,6 @@ "redis": "^4.2.0", "response-time": "^2.3.2", "socket.io": "^4.3.1", - "swagger-express-validator": "^1.0.2", - "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^4.6.0", "validator": "^13.7.0", "winston": "^3.8.1", @@ -138,46 +136,6 @@ "node": ">=6.0.0" } }, - "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", - "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", - "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.6", - "call-me-maybe": "^1.0.1", - "js-yaml": "^4.1.0" - } - }, - "node_modules/@apidevtools/openapi-schemas": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", - "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@apidevtools/swagger-methods": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", - "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" - }, - "node_modules/@apidevtools/swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "^9.0.6", - "@apidevtools/openapi-schemas": "^2.0.4", - "@apidevtools/swagger-methods": "^3.0.2", - "@jsdevtools/ono": "^7.1.3", - "call-me-maybe": "^1.0.1", - "z-schema": "^5.0.1" - }, - "peerDependencies": { - "openapi-types": ">=7" - } - }, "node_modules/@aws-crypto/ie11-detection": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", @@ -3522,11 +3480,6 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" - }, "node_modules/@jsdoc/salty": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", @@ -4560,11 +4513,6 @@ "hoist-non-react-statics": "^3.3.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" - }, "node_modules/@types/jsonwebtoken": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", @@ -5382,11 +5330,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/call-me-maybe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -8312,11 +8255,6 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" - }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -8327,11 +8265,6 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" - }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -8362,11 +8295,6 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" - }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -9223,12 +9151,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/openapi-types": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz", - "integrity": "sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==", - "peer": true - }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -11267,128 +11189,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/swagger-express-validator": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/swagger-express-validator/-/swagger-express-validator-1.0.2.tgz", - "integrity": "sha512-uNP3wjzAJTyu0CcXq2O8yJPCDcRLMR/amf0oRkN8syRBqUIQhBcYFpyQC+1PR1TNa8gxkhWdpjaDG3bbCKZ0dA==", - "dependencies": { - "ajv": "^6.10.2", - "debug": "3.2.6", - "lodash": "^4.17.15", - "path-to-regexp": "2.4.0", - "validator": "10.11.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/swagger-express-validator/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/swagger-express-validator/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/swagger-express-validator/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/swagger-express-validator/node_modules/path-to-regexp": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", - "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==" - }, - "node_modules/swagger-express-validator/node_modules/validator": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", - "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/swagger-jsdoc": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", - "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", - "dependencies": { - "commander": "6.2.0", - "doctrine": "3.0.0", - "glob": "7.1.6", - "lodash.mergewith": "^4.6.2", - "swagger-parser": "^10.0.3", - "yaml": "2.0.0-1" - }, - "bin": { - "swagger-jsdoc": "bin/swagger-jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/swagger-jsdoc/node_modules/commander": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", - "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/swagger-jsdoc/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/swagger-jsdoc/node_modules/yaml": { - "version": "2.0.0-1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", - "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", - "dependencies": { - "@apidevtools/swagger-parser": "10.0.3" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/swagger-ui-dist": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.18.1.tgz", @@ -12198,34 +11998,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/z-schema": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", - "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", - "dependencies": { - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "validator": "^13.7.0" - }, - "bin": { - "z-schema": "bin/z-schema" - }, - "engines": { - "node": ">=8.0.0" - }, - "optionalDependencies": { - "commander": "^9.4.1" - } - }, - "node_modules/z-schema/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "optional": true, - "engines": { - "node": "^12.20.0 || >=14" - } } }, "dependencies": { @@ -12294,40 +12066,6 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, - "@apidevtools/json-schema-ref-parser": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", - "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", - "requires": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.6", - "call-me-maybe": "^1.0.1", - "js-yaml": "^4.1.0" - } - }, - "@apidevtools/openapi-schemas": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", - "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==" - }, - "@apidevtools/swagger-methods": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", - "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" - }, - "@apidevtools/swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", - "requires": { - "@apidevtools/json-schema-ref-parser": "^9.0.6", - "@apidevtools/openapi-schemas": "^2.0.4", - "@apidevtools/swagger-methods": "^3.0.2", - "@jsdevtools/ono": "^7.1.3", - "call-me-maybe": "^1.0.1", - "z-schema": "^5.0.1" - } - }, "@aws-crypto/ie11-detection": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", @@ -14846,11 +14584,6 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, - "@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" - }, "@jsdoc/salty": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", @@ -15602,11 +15335,6 @@ "hoist-non-react-statics": "^3.3.0" } }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" - }, "@types/jsonwebtoken": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", @@ -16259,11 +15987,6 @@ "get-intrinsic": "^1.0.2" } }, - "call-me-maybe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -18471,11 +18194,6 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" - }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -18486,11 +18204,6 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" - }, "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -18521,11 +18234,6 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" - }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -19178,12 +18886,6 @@ "mimic-fn": "^2.1.0" } }, - "openapi-types": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz", - "integrity": "sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==", - "peer": true - }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -20722,100 +20424,6 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, - "swagger-express-validator": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/swagger-express-validator/-/swagger-express-validator-1.0.2.tgz", - "integrity": "sha512-uNP3wjzAJTyu0CcXq2O8yJPCDcRLMR/amf0oRkN8syRBqUIQhBcYFpyQC+1PR1TNa8gxkhWdpjaDG3bbCKZ0dA==", - "requires": { - "ajv": "^6.10.2", - "debug": "3.2.6", - "lodash": "^4.17.15", - "path-to-regexp": "2.4.0", - "validator": "10.11.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "path-to-regexp": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", - "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==" - }, - "validator": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", - "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" - } - } - }, - "swagger-jsdoc": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", - "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", - "requires": { - "commander": "6.2.0", - "doctrine": "3.0.0", - "glob": "7.1.6", - "lodash.mergewith": "^4.6.2", - "swagger-parser": "^10.0.3", - "yaml": "2.0.0-1" - }, - "dependencies": { - "commander": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", - "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==" - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "yaml": { - "version": "2.0.0-1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", - "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==" - } - } - }, - "swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", - "requires": { - "@apidevtools/swagger-parser": "10.0.3" - } - }, "swagger-ui-dist": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.18.1.tgz", @@ -21412,25 +21020,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" - }, - "z-schema": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", - "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", - "requires": { - "commander": "^9.4.1", - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "validator": "^13.7.0" - }, - "dependencies": { - "commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "optional": true - } - } } } } diff --git a/package.json b/package.json index fe8eba1c..b4bf463e 100644 --- a/package.json +++ b/package.json @@ -56,4 +56,4 @@ }, "author": "sparcs/taxi", "license": "MIT" -} \ No newline at end of file +} diff --git a/src/routes/auth.js b/src/routes/auth.js index 58d3bcbd..1738affb 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -19,14 +19,6 @@ router.route("/app/token/refresh").get(mobileAuthHandlers.refreshAccessToken); router.route("/app/device").post(mobileAuthHandlers.registerDeviceTokenHandler); router.route("/app/device").delete(mobileAuthHandlers.removeDeviceTokenHandler); router.route("/app/token/generate").get(authHandlers.generateTokenHandler); -// FCM 토큰 등록 -router.post( - "/registerDeviceToken", - authMiddleware, - [body("deviceToken").isString().isLength({ min: 1, max: 1024 })], - validator, - authHandlers.registerDeviceTokenHandler -); // 환경변수 SPARCSSSO_CLIENT_ID 유무에 따라 로그인 방식이 변경됩니다. module.exports = sparcsssoEnv?.id ? router : authReplace; diff --git a/src/routes/auth.replace.js b/src/routes/auth.replace.js index 8ea71de0..6507a7e8 100755 --- a/src/routes/auth.replace.js +++ b/src/routes/auth.replace.js @@ -15,13 +15,4 @@ router.route("/sparcssso").get(authReplaceHandlers.sparcsssoHandler); // 로그아웃 router.route("/logout").get(authReplaceHandlers.logoutHandler); -// FCM 토큰 등록 -router.post( - "/registerDeviceToken", - authMiddleware, - [body("deviceToken").isString().isLength({ min: 1, max: 1024 })], - validator, - authReplaceHandlers.registerDeviceTokenHandler -); - module.exports = router; diff --git a/src/routes/notifications.js b/src/routes/notifications.js index 285368d6..e63e7994 100644 --- a/src/routes/notifications.js +++ b/src/routes/notifications.js @@ -8,6 +8,16 @@ const validator = require("../middlewares/validator"); // 라우터 접근 시 로그인 필요 router.use(require("../middlewares/auth")); +// FCM 토큰 등록 +router.post( + "/registerDeviceToken", + [body("deviceToken").isString().isLength({ min: 1, max: 1024 })], + validator, + notificationHandlers.registerDeviceTokenHandler +); + +// @TODO: "/editOptions" + router.get( "/options", query("deviceToken").isString(), diff --git a/src/services/auth.js b/src/services/auth.js index ec588be9..66c986a7 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -9,11 +9,7 @@ const { userModel } = require("../modules/stores/mongo"); const { user: userPattern } = require("../modules/patterns"); const { getLoginInfo, logout, login } = require("../modules/auths/login"); -const { - registerDeviceToken, - unregisterDeviceToken, - validateDeviceToken, -} = require("../modules/fcm"); +const { unregisterDeviceToken } = require("../modules/fcm"); const logger = require("../modules/logger"); const { generateNickname, @@ -177,6 +173,7 @@ const logoutHandler = async (req, res) => { await unregisterDeviceToken(deviceToken); } + // 로그아웃 URL을 생성 및 반환 const redirectUrl = frontUrl + "/login"; const ssoLogoutUrl = client.getLogoutUrl(sid, redirectUrl); logout(req, res); @@ -186,37 +183,9 @@ const logoutHandler = async (req, res) => { } }; -const registerDeviceTokenHandler = async (req, res) => { - try { - // 해당 FCM device token이 유효한지 검사합니다. - const deviceToken = req.body.deviceToken; - const isValid = await validateDeviceToken(deviceToken); - if (!isValid) { - return res - .status(400) - .send("Auth/registerDeviceToken : deviceToken is invalid"); - } - - // 데이터베이스에 deviceToken 레코드를 추가합니다. - const user = await userModel.findOne({ id: req.userId }, "_id"); - const newDeviceToken = await registerDeviceToken(user._id, deviceToken); - - // 세션에 현재 사용자 기기의 deviceToken을 저장합니다. - req.session.deviceToken = deviceToken; - - return res.status(200).json({ - deviceToken: newDeviceToken, - }); - } catch (e) { - logger.error(e); - res.status(500).send("Auth/registerDeviceToken : internal server error"); - } -}; - module.exports = { sparcsssoHandler, sparcsssoCallbackHandler, logoutHandler, generateTokenHandler, - registerDeviceTokenHandler, }; diff --git a/src/services/auth.replace.js b/src/services/auth.replace.js index 7a98242a..596de399 100644 --- a/src/services/auth.replace.js +++ b/src/services/auth.replace.js @@ -138,6 +138,7 @@ const logoutHandler = async (req, res) => { await unregisterDeviceToken(deviceToken); } + // sparcs-sso 로그아웃 URL을 생성 및 반환 const ssoLogoutUrl = frontUrl + "/login"; logout(req, res); res.json({ ssoLogoutUrl }); diff --git a/src/services/notifications.js b/src/services/notifications.js index db533920..b2a32c3a 100644 --- a/src/services/notifications.js +++ b/src/services/notifications.js @@ -1,24 +1,62 @@ +const { userModel } = require("../modules/stores/mongo"); const { notificationOptionModel } = require("../modules/stores/mongo"); const logger = require("../modules/logger"); +const { registerDeviceToken, validateDeviceToken } = require("../modules/fcm"); + +const registerDeviceTokenHandler = async (req, res) => { + try { + // 해당 FCM device token이 유효한지 검사합니다. + const { deviceToken } = req.body; + const isValid = await validateDeviceToken(deviceToken); + if (!isValid) { + return res + .status(400) + .send("Notifications/registerDeviceToken : deviceToken is invalid"); + } + + // 데이터베이스에 deviceToken 레코드를 추가합니다. + const user = await userModel.findOne({ id: req.userId }, "_id"); + const newDeviceToken = await registerDeviceToken(user._id, deviceToken); + + // 세션에 현재 사용자 기기의 deviceToken을 저장합니다. + req.session.deviceToken = deviceToken; + + return res.status(200).json({ + deviceToken: newDeviceToken, + }); + } catch (e) { + logger.error(e); + res + .status(500) + .send("Notifications/registerDeviceToken : internal server error"); + } +}; + const optionsHandler = async (req, res) => { try { const { deviceToken } = req.query; + // 세션에 저장된 deviceToken과 요청에 들어온 deviceToken이 일치하는지 검사합니다. + if (req.session.deviceToken !== deviceToken) { + return res + .status(400) + .send("Notifications/options : deviceToken is invalid"); + } + // deviceToken에 대응되는 알림 설정을 찾아 반환합니다. const notificationOptions = await notificationOptionModel .findOne( - { - deviceToken, - }, + { deviceToken }, "-_id chatting keywords beforeDepart notice advertisement" ) .lean(); if (!notificationOptions) { return res .status(400) - .send("Notificaiton/getNotificationOptions: deviceToken not found"); + .send("Notificaiton/options: deviceToken not found"); } + res.status(200).json(notificationOptions); } catch (err) { logger.error(err); @@ -86,6 +124,7 @@ const editOptionsHandler = async (req, res) => { }; module.exports = { + registerDeviceTokenHandler, optionsHandler, editOptionsHandler, }; From 6d062ab45bdda9808d81962a60f2036cafc10a14 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Fri, 31 Mar 2023 21:56:41 +0900 Subject: [PATCH 191/308] Fix: notifications service --- src/routes/notifications.js | 2 -- src/services/notifications.js | 38 +++++++++++++---------------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/routes/notifications.js b/src/routes/notifications.js index e63e7994..1a8124b5 100644 --- a/src/routes/notifications.js +++ b/src/routes/notifications.js @@ -16,8 +16,6 @@ router.post( notificationHandlers.registerDeviceTokenHandler ); -// @TODO: "/editOptions" - router.get( "/options", query("deviceToken").isString(), diff --git a/src/services/notifications.js b/src/services/notifications.js index b2a32c3a..13204f80 100644 --- a/src/services/notifications.js +++ b/src/services/notifications.js @@ -38,7 +38,7 @@ const optionsHandler = async (req, res) => { const { deviceToken } = req.query; // 세션에 저장된 deviceToken과 요청에 들어온 deviceToken이 일치하는지 검사합니다. - if (req.session.deviceToken !== deviceToken) { + if (req.session?.deviceToken !== deviceToken) { return res .status(400) .send("Notifications/options : deviceToken is invalid"); @@ -70,6 +70,13 @@ const editOptionsHandler = async (req, res) => { try { const { deviceToken, options } = req.body; + // 세션에 저장된 deviceToken과 요청에 들어온 deviceToken이 일치하는지 검사합니다. + if (req.session?.deviceToken !== deviceToken) { + return res + .status(400) + .send("Notifications/editOptions : deviceToken is invalid"); + } + // FIXME : can refactor with using reduce const newOptions = {}; const booleanFields = [ @@ -87,39 +94,22 @@ const editOptionsHandler = async (req, res) => { newOptions.keywords = options.keywords; } - await notificationOptionModel.updateOne( - { - deviceToken, - }, - { - deviceToken, - ...newOptions, - }, - { - new: true, - } - ); - const updatedNotificationOptions = await notificationOptionModel - .findOne( - { - deviceToken, - }, - "-_id chatting keywords beforeDepart notice advertisement" - ) + .findOneAndUpdate({ deviceToken }, newOptions, { new: true }) .lean(); - if (!updatedNotificationOptions) - res + if (!updatedNotificationOptions) { + return res .status(400) - .send("Notification/changeNotificationOptions: deviceToken not found"); + .send("Notification/editOptions: deviceToken not found"); + } res.status(200).json(updatedNotificationOptions); } catch (err) { logger.error(err); return res .status(500) - .send("Notification/changeNotificationOptions: internal server error"); + .send("Notification/editOptions: internal server error"); } }; From 0d5876d33dafdacfa504d973fc9b8b268bc5d70c Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Sun, 2 Apr 2023 02:06:19 +0900 Subject: [PATCH 192/308] Fix: getTokensOfUsers --- src/modules/fcm.js | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index 17dc1cc2..efe715e2 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -149,18 +149,27 @@ const validateDeviceToken = async (deviceToken) => { * @return {Promise>} deviceToken의 Array를 반환합니다. 오류가 발생하면 빈 배열을 반환합니다. */ const getTokensOfUsers = async (userIds, notificationOptions = {}) => { - const deviceTokensOfUsers = await Promise.all( - userIds.map(async (userId) => { - const query = { userId }; - if (notificationOptions) query.notificationOptions = notificationOptions; - const deviceToken = await deviceTokenModel.findOne(query); - return deviceToken?.deviceTokens || new Array(); - }) - ); - return deviceTokensOfUsers.reduce( - (arrayA, arrayB) => arrayA.concat(arrayB), - new Array() - ); + const deviceTokensOfUsers = ( + await Promise.all( + userIds.map( + async (userId) => + (await deviceTokenModel.findOne({ userId }))?.deviceTokens || [] + ) + ) + ).flat(); + const deviceTokensWithOptions = ( + await Promise.all( + deviceTokensOfUsers.map(async (deviceToken) => + (await notificationOptionModel.findOne({ + deviceToken, + ...notificationOptions, + })) + ? [deviceToken] + : [] + ) + ) + ).flat(); + return deviceTokensWithOptions; }; /** From a6cac64fdc28c5565ce7e8cc83b7de0cb417e4a6 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Sun, 2 Apr 2023 02:46:57 +0900 Subject: [PATCH 193/308] Fix: registerDeviceToken --- src/modules/fcm.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/modules/fcm.js b/src/modules/fcm.js index efe715e2..325bf8a3 100644 --- a/src/modules/fcm.js +++ b/src/modules/fcm.js @@ -31,11 +31,15 @@ const initializeApp = () => { */ const registerDeviceToken = async (userId, deviceToken) => { try { + // 디바이스 토큰을 다른 사용자가 사용하고 있는지 확인 및 삭제합니다. + await deviceTokenModel.updateMany( + { userId: { $ne: userId }, deviceTokens: deviceToken }, + { $pull: { deviceTokens: deviceToken } } + ); + // 디바이스 토큰을 DB에 추가합니다. const newDeviceToken = await deviceTokenModel.findOneAndUpdate( - { - userId, - }, + { userId }, { userId, $addToSet: { deviceTokens: deviceToken }, From af572941848098d48cb3b97c7eae0dd5ce2ea6c6 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Mon, 3 Apr 2023 00:07:25 +0900 Subject: [PATCH 194/308] Fix: TEST_ACCOUNTS --- loadenv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loadenv.js b/loadenv.js index 6c1e887e..a73d1fef 100644 --- a/loadenv.js +++ b/loadenv.js @@ -34,5 +34,5 @@ module.exports = { process.env.GOOGLE_APPLICATION_CREDENTIALS && JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS), testAccounts: - process.env.testAccounts && JSON.parse(process.env.TEST_ACCOUNTS), + process.env.TEST_ACCOUNTS && JSON.parse(process.env.TEST_ACCOUNTS), }; From f70b43dd863d15523a340295065cd9d7d1c3d161 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 4 Apr 2023 22:37:49 +0900 Subject: [PATCH 195/308] Remove: unused modules import --- src/routes/auth.js | 3 --- src/routes/auth.replace.js | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/routes/auth.js b/src/routes/auth.js index 1738affb..11ea325b 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -1,9 +1,6 @@ const express = require("express"); const router = express.Router(); -const { body } = require("express-validator"); -const authMiddleware = require("../middlewares/auth"); -const validator = require("../middlewares/validator"); const authHandlers = require("../services/auth"); const mobileAuthHandlers = require("../services/auth.mobile"); diff --git a/src/routes/auth.replace.js b/src/routes/auth.replace.js index 6507a7e8..8ea4fced 100755 --- a/src/routes/auth.replace.js +++ b/src/routes/auth.replace.js @@ -1,10 +1,7 @@ const express = require("express"); const router = express.Router(); -const { body } = require("express-validator"); const authReplaceHandlers = require("../services/auth.replace"); -const authMiddleware = require("../middlewares/auth"); -const validator = require("../middlewares/validator"); // 로그인 시도 router.route("/try").post(authReplaceHandlers.tryHandler); From 4aa99c095a6cab7ec3f0986bbe63b099c60f5970 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Tue, 4 Apr 2023 22:43:55 +0900 Subject: [PATCH 196/308] Refactor: loginHtml --- src/services/auth.replace.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/services/auth.replace.js b/src/services/auth.replace.js index 596de399..bb39f1a3 100644 --- a/src/services/auth.replace.js +++ b/src/services/auth.replace.js @@ -2,10 +2,7 @@ const { frontUrl } = require("../../loadenv"); const { userModel } = require("../modules/stores/mongo"); const { logout, login } = require("../modules/auths/login"); -const { - registerDeviceToken, - unregisterDeviceToken, -} = require("../modules/fcm"); +const { unregisterDeviceToken } = require("../modules/fcm"); const { generateNickname, generateProfileImageUrl, @@ -47,6 +44,7 @@ const loginHtml = ` } $('#btn').click(submitHandler); $('#input-id').on("keyup", enterHandler); + $('#input-id').focus(); }); @@ -120,10 +118,8 @@ const loginDone = (req, res, userData) => { }; const tryHandler = (req, res) => { - { - const id = req.body.id || req.query.id; - loginDone(req, res, makeInfo(id)); - } + const id = req.body.id || req.query.id; + loginDone(req, res, makeInfo(id)); }; const sparcsssoHandler = (req, res) => { From 613235a502621ae782d6319de16866a0e3ff3444 Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 4 Apr 2023 22:45:47 +0900 Subject: [PATCH 197/308] Add: add deviceToken to loginInfo/detail response --- src/services/logininfo.js | 57 ++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/services/logininfo.js b/src/services/logininfo.js index ddac7b8c..12ec642b 100644 --- a/src/services/logininfo.js +++ b/src/services/logininfo.js @@ -1,38 +1,41 @@ const { userModel } = require("../modules/stores/mongo"); const { getLoginInfo } = require("../modules/auths/login"); +const logger = require("../modules/logger"); const logininfoHandler = (req, res) => { const user = getLoginInfo(req); res.json(user); }; -const detailHandler = (req, res) => { - const user = getLoginInfo(req); - if (!user.id) return res.json({ id: undefined }); - userModel.findOne( - { id: user.id }, - "_id name nickname id withdraw ban joinat agreeOnTermsOfService subinfo email profileImageUrl account", - (err, result) => { - if (err) res.json({ err: true }); - else if (!result) res.json({ err: true }); - else { - res.json({ - oid: result._id, - id: result.id, - name: result.name, - nickname: result.nickname, - withdraw: result.withdraw, - ban: result.ban, - joinat: result.joinat, - agreeOnTermsOfService: result.agreeOnTermsOfService, - subinfo: result.subinfo, - email: result.email, - profileImgUrl: result.profileImageUrl, - account: result.account ? result.account : "", - }); - } - } - ); +const detailHandler = async (req, res) => { + try { + const user = getLoginInfo(req); + if (!user.id) return res.json({ id: undefined }); + + const userDetail = await userModel.findOne( + { id: user.id }, + "_id name nickname id withdraw ban joinat agreeOnTermsOfService subinfo email profileImageUrl account" + ); + + res.json({ + oid: userDetail._id, + id: userDetail.id, + name: userDetail.name, + nickname: userDetail.nickname, + withdraw: userDetail.withdraw, + ban: userDetail.ban, + joinat: userDetail.joinat, + agreeOnTermsOfService: userDetail.agreeOnTermsOfService, + subinfo: userDetail.subinfo, + email: userDetail.email, + profileImgUrl: userDetail.profileImageUrl, + account: userDetail.account ? userDetail.account : "", + deviceToken: req.session?.deviceToken, + }); + } catch (error) { + logger.error(error); + res.json({ err: true }); + } }; module.exports = { From fdfd4328d639baea58ab59371f794f02e2839d7f Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Tue, 4 Apr 2023 13:53:52 +0000 Subject: [PATCH 198/308] Add: socket connection & disconnection --- src/modules/auths/login.js | 14 ++-- src/services/socket.chats.js | 135 +++++------------------------------ 2 files changed, 23 insertions(+), 126 deletions(-) diff --git a/src/modules/auths/login.js b/src/modules/auths/login.js index 3675d2e0..0b38e3d9 100644 --- a/src/modules/auths/login.js +++ b/src/modules/auths/login.js @@ -26,23 +26,21 @@ const login = (req, sid, id, name) => { const logout = (req) => { // 로그아웃 전 socket.io 소켓들 연결부터 끊기 - if (req.session.socketId && req.session.chatRoomId) { + if (req.session.socketId) { req.app.get("io").in(req.session.socketId).disconnectSockets(true); - leaveChatRoom(req); + disconnectUser(req); } req.session.destroy((err) => { if (err) logger.error(err); }); }; -const joinChatRoom = (req, socketId, roomId) => { +const connectUser = (req, socketId) => { req.session.socketId = socketId; - req.session.chatRoomId = roomId; }; -const leaveChatRoom = (req) => { +const disconnectUser = (req) => { req.session.socketId = null; - req.session.chatRoomId = null; }; module.exports = { @@ -50,6 +48,6 @@ module.exports = { isLogin, login, logout, - joinChatRoom, - leaveChatRoom, + connectUser, + disconnectUser, }; diff --git a/src/services/socket.chats.js b/src/services/socket.chats.js index 717026d9..3bf05107 100644 --- a/src/services/socket.chats.js +++ b/src/services/socket.chats.js @@ -1,8 +1,4 @@ -const { - getLoginInfo, - joinChatRoom, - leaveChatRoom, -} = require("../modules/auths/login"); +const { connectUser, disconnectUser } = require("../modules/auths/login"); const { roomModel, userModel, chatModel } = require("../modules/stores/mongo"); const { chatPopulateOption } = require("../modules/populates/chats"); const { getS3Url } = require("../modules/stores/awsS3"); @@ -164,135 +160,38 @@ const transformChatsForRoom = async (chats) => { return chatsToSend; }; -const ioListeners = (io, socket) => { +const ioListeners = (socket) => { const session = socket.handshake.session; - // 사용자가 Socket.io 서버와 연결될 때마다 발생하는 이벤트 - socket.on("chats-join", async (roomId) => { + socket.on("connection", async (userId) => { try { - const myUserId = getLoginInfo({ session: session }).id || ""; - const myUser = await userModel.findOne({ id: myUserId }, "_id id"); + const myUser = await userModel.findOne({ id: userId }, "_id id"); if (!myUser) - return io.to(socket.id).emit("chats-join", { err: "user not exist" }); + /* TODO: ERROR HANDLE */ + return; - const room = await roomModel.findById(roomId, "part"); - if (!room) { - return io.to(socket.id).emit("chats-join", { err: "room not exist" }); - } - // If the user didn't participate in the room - if (!room.part.indexOf(myUser._id) === -1) { - return io.to(socket.id).emit("chats-join", { err: "user not joined" }); - } - - // join chat room - joinChatRoom({ session: session }, socket.id, roomId); - socket.join(`chatRoom-${roomId}`); + // connect to User + connectUser({ session }, socket.id); + socket.join(`user-${userId}`); session.save(); // Socket.io 세션의 변경 사항을 Express 세션에 반영. - - const amount = 30; - const chats = await chatModel - .find({ roomId: roomId, isValid: true }) - .sort({ time: -1 }) - .limit(amount) - .lean() - .populate(chatPopulateOption); - - if (chats) { - chats.reverse(); - io.to(socket.id).emit("chats-join", { - chats: await transformChatsForRoom(chats), - }); - } - } catch (err) { - logger.error(err); - io.to(socket.id).emit("chats-join", { err: true }); - } - }); - - // 사용자와 Socket.io 서버의 연결이 끊어졌을 때 발생하는 이벤트 - socket.on("chats-disconnect", async () => { - try { - const myUserId = getLoginInfo({ session: session }).id || ""; - const myUser = await userModel.findOne({ id: myUserId }, "_id nickname"); - if (!myUser) - return io - .to(socket.id) - .emit("chats-disconnect", { err: "user not exist" }); - - const roomId = session.chatRoomId; - if (!roomId) - return io - .to(socket.id) - .emit("chats-disconnect", { err: "user not join chat room" }); - - // leave chat room - leaveChatRoom({ session: session }); - socket.leave(`chatRoom-${roomId}`); } catch (err) { logger.error(err); - io.to(socket.id).emit("chats-disconnect", { err: true }); + /* TODO: ERROR HANDLE PART */ } }); - // 사용자가 채팅 메시지를 전송했을 때 발생하는 이벤트 - socket.on("chats-send", async (chatMessage) => { + socket.on("disconnection", async (userId) => { try { - const myUserId = getLoginInfo({ session: session }).id || ""; - const myUser = await userModel.findOne( - { id: myUserId }, - "_id id nickname profileImageUrl" - ); + const myUser = await userModel.findOne({ id: userId }, "_id id"); if (!myUser) - return io.to(socket.id).emit("chats-send", { err: "user not exist" }); - const roomId = session.chatRoomId; - if (!roomId) - return io - .to(socket.id) - .emit("chats-send", { err: "user not join chat room" }); - await emitChatEvent(io, roomId, { - type: chatMessage.type || "text", - content: chatMessage.content, - authorId: myUser._id, - }); - io.to(socket.id).emit("chats-send", { done: true }); - } catch (err) { - logger.error(err); - io.to(socket.id).emit("chats-send", { err: true }); - } - }); - - // 사용자가 과거 채팅 메시지를 로드하려 할 때 발생하는 이벤트 - socket.on("chats-load", async (lastDate, amount) => { - try { - const roomId = session.chatRoomId; - // 클라이언트로부터 받은 lastDate가 유효한 날짜 문자열일 때만 쿼리를 수행 - if (lastDate && validator.isISO8601(lastDate)) { - // 새로 불러올 메시지 수는 기본 30, 사용자가 입력한 값이 유효하면 그 값을 사용 - if (validator.isInt(String(amount), { min: 1, max: 50 })) { - amount = Number(amount); - } else { - amount = 30; - } - - const chats = await chatModel - .find({ roomId, time: { $lt: lastDate } }) - .sort({ time: -1 }) - .limit(amount) - .lean() - .populate(chatPopulateOption); + /* TODO: ERROR HANDLE */ + return; - if (chats) { - chats.reverse(); - io.to(socket.id).emit("chats-load", { - chats: await transformChatsForRoom(chats), - }); - } - } else { - return io.to(socket.id).emit("chats-load", { err: true }); - } + disconnectUser({ session }); + socket.leave(`user-${userId}`); } catch (err) { logger.error(err); - io.to(socket.id).emit("chats-load", { err: true }); + /* TODO: ERROR HANDLE PART */ } }); }; From a1ca6ec496b79e25d04a040e4f03105913c78230 Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 4 Apr 2023 22:55:46 +0900 Subject: [PATCH 199/308] Refactor: remove deviceToken from notificationOption APIs --- src/routes/notifications.js | 7 +------ src/services/notifications.js | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/routes/notifications.js b/src/routes/notifications.js index 1a8124b5..dd666953 100644 --- a/src/routes/notifications.js +++ b/src/routes/notifications.js @@ -16,15 +16,10 @@ router.post( notificationHandlers.registerDeviceTokenHandler ); -router.get( - "/options", - query("deviceToken").isString(), - notificationHandlers.optionsHandler -); +router.get("/options", notificationHandlers.optionsHandler); router.post( "/editOptions", - body("deviceToken").isString(), body("options").isObject(), body("options.chatting").optional().isBoolean(), body("options.keywords").optional().isArray(), diff --git a/src/services/notifications.js b/src/services/notifications.js index 13204f80..799c8bb7 100644 --- a/src/services/notifications.js +++ b/src/services/notifications.js @@ -35,13 +35,12 @@ const registerDeviceTokenHandler = async (req, res) => { const optionsHandler = async (req, res) => { try { - const { deviceToken } = req.query; - - // 세션에 저장된 deviceToken과 요청에 들어온 deviceToken이 일치하는지 검사합니다. - if (req.session?.deviceToken !== deviceToken) { + // 세션에 deviceToken이 저장되어 있는지 검사합니다. + const { deviceToken } = req.session; + if (!deviceToken) { return res .status(400) - .send("Notifications/options : deviceToken is invalid"); + .send("Notifications/options : deviceToken not found"); } // deviceToken에 대응되는 알림 설정을 찾아 반환합니다. @@ -54,7 +53,7 @@ const optionsHandler = async (req, res) => { if (!notificationOptions) { return res .status(400) - .send("Notificaiton/options: deviceToken not found"); + .send("Notificaiton/options: notificationOption not found"); } res.status(200).json(notificationOptions); @@ -68,13 +67,14 @@ const optionsHandler = async (req, res) => { const editOptionsHandler = async (req, res) => { try { - const { deviceToken, options } = req.body; + const { options } = req.body; - // 세션에 저장된 deviceToken과 요청에 들어온 deviceToken이 일치하는지 검사합니다. - if (req.session?.deviceToken !== deviceToken) { + // 세션에 deviceToken이 저장되어 있는지 검사합니다. + const { deviceToken } = req.session; + if (!deviceToken) { return res .status(400) - .send("Notifications/editOptions : deviceToken is invalid"); + .send("Notifications/options : deviceToken not found"); } // FIXME : can refactor with using reduce From a65ea11c117fcd51db81664a2d811a908934f971 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 5 Apr 2023 00:53:07 +0900 Subject: [PATCH 200/308] Add: redirect to auth.replace --- src/routes/auth.js | 2 ++ src/routes/auth.replace.js | 24 +++++++++++++++++++++--- src/services/auth.replace.js | 29 +++++++++++++++++------------ 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/routes/auth.js b/src/routes/auth.js index 11ea325b..d6feb44c 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -1,5 +1,7 @@ const express = require("express"); const router = express.Router(); +const { query } = require("express-validator"); +const validator = require("../middlewares/validator"); const authHandlers = require("../services/auth"); const mobileAuthHandlers = require("../services/auth.mobile"); diff --git a/src/routes/auth.replace.js b/src/routes/auth.replace.js index 8ea4fced..d55013d6 100755 --- a/src/routes/auth.replace.js +++ b/src/routes/auth.replace.js @@ -1,15 +1,33 @@ const express = require("express"); const router = express.Router(); +const { body, query } = require("express-validator"); +const validator = require("../middlewares/validator"); const authReplaceHandlers = require("../services/auth.replace"); // 로그인 시도 -router.route("/try").post(authReplaceHandlers.tryHandler); +router.post( + "/try", + body("id").isString(), + body("redirect").optional().isString(), + validator, + authReplaceHandlers.tryHandler +); // html 로그인 페이지 쏴주기 -router.route("/sparcssso").get(authReplaceHandlers.sparcsssoHandler); +router.get( + "/sparcssso", + query("redirect").optional().isString(), + validator, + authReplaceHandlers.sparcsssoHandler +); // 로그아웃 -router.route("/logout").get(authReplaceHandlers.logoutHandler); +router.get( + "/logout", + query("redirect").optional().isString(), + validator, + authReplaceHandlers.logoutHandler +); module.exports = router; diff --git a/src/services/auth.replace.js b/src/services/auth.replace.js index bb39f1a3..d21b8cb1 100644 --- a/src/services/auth.replace.js +++ b/src/services/auth.replace.js @@ -11,7 +11,7 @@ const logger = require("../modules/logger"); const { registerDeviceTokenHandler } = require("../services/auth"); -const loginHtml = ` +const loginHtmlBuilder = (redirectPath) => ` @@ -21,7 +21,7 @@ const loginHtml = ` - - - -
아이디 입력
- -
로그인
- - -`; +const loginReplacePage = require("../views/loginReplacePage"); const makeInfo = (id) => { const info = { @@ -144,7 +97,7 @@ const sparcsssoHandler = (req, res) => { const isApp = !!req.query.isApp; req.session.isApp = isApp; - res.end(loginHtmlBuilder(redirectPath)); + res.end(loginReplacePage(redirectPath)); }; const logoutHandler = async (req, res) => { diff --git a/src/views/loginReplacePage.js b/src/views/loginReplacePage.js new file mode 100644 index 00000000..c41c6025 --- /dev/null +++ b/src/views/loginReplacePage.js @@ -0,0 +1,47 @@ +module.exports = (redirectPath) => ` + + + + replace Login + + + + + + +
아이디 입력
+ +
로그인
+ + +`; From a651908cfe397cee03458ea35a9f0212a5fa731d Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 19 Jul 2023 21:07:01 +0900 Subject: [PATCH 264/308] Add: emailNoSettlementPage --- src/views/emailNoSettlementPage.js | 31 +++++++++++++ src/views/emailReportPage.js | 72 ++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/views/emailNoSettlementPage.js create mode 100644 src/views/emailReportPage.js diff --git a/src/views/emailNoSettlementPage.js b/src/views/emailNoSettlementPage.js new file mode 100644 index 00000000..f58599c1 --- /dev/null +++ b/src/views/emailNoSettlementPage.js @@ -0,0 +1,31 @@ +const { frontUrl } = require("../../loadenv"); +const emailReportPage = require("./emailReportPage"); + +module.exports = (name, nickname, roomName, payer, roomId) => + emailReportPage( + "미정산 내역 관련 안내", + `${name} (${nickname}) 님께

+ 안녕하세요, ${name} (${nickname}) 님.
+ KAIST 학부 총학생회 산하 특별기구 SPARCS의 Taxi 팀입니다.

+ 최근 참여하신 방에서 정산이 이루어지지 않았다는 사용자의 문의가 접수되어 메일을 보내드립니다.

+
+ 위 방에서 채팅을 확인하실 수 있으며, 결제하신 분께 해당 금액을 정산해주시기를 부탁드립니다.
+ 미정산이 반복되는 경우 Taxi 서비스 이용이 제한될 수 있음을 알려드립니다.
+ 문의가 필요하신 경우, 택시 서비스 내부의 "채널톡 문의하기" 혹은 메일 회신 주시면 됩니다.

+ 감사합니다.
+ SPARCS Taxi팀 드림. + ` + ); diff --git a/src/views/emailReportPage.js b/src/views/emailReportPage.js new file mode 100644 index 00000000..bffb995f --- /dev/null +++ b/src/views/emailReportPage.js @@ -0,0 +1,72 @@ +const { getS3Url } = require("../modules/stores/aws"); + +module.exports = (title, content) => ` +`; From 81b7531ca10d0505df59db1c3ab10d08e1485e76 Mon Sep 17 00:00:00 2001 From: Hyogyeong8 Date: Wed, 19 Jul 2023 21:19:39 +0900 Subject: [PATCH 265/308] add: contents --- src/views/email copy.html | 32 ++++++++++++++++++++++++++++---- src/views/email.js | 20 -------------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/views/email copy.html b/src/views/email copy.html index 28716938..fc479dc4 100644 --- a/src/views/email copy.html +++ b/src/views/email copy.html @@ -24,10 +24,12 @@ text-align: left; color: #FFFFFF; } + .body { display: flex; width: 800px; } +
@@ -37,10 +39,32 @@
Taxi
-
-
- 미정산 내역 관리 안내 +
+
미정산 내역 관리 안내
+
+ OOO (까다로운 기초항공프로젝트_OOOOO) 님께 + + 안녕하세요, OOO (까다로운 기초항공프로젝트_OOOOO) 님. + KAIST 학부 총학생회 산하 특별기구 SPARCS의 Taxi 팀입니다. + + 최근 참여하신 방에서 정산이 이루어지지 않았다는 사용자의 문의가 접수되어 메일을 보내드립니다. + + 방 제목: 환경을 생각하는 동승 + 결제자: 깜찍한 제조 프로세스 혁신_OOOOO + 링크: https://taxi.sparcs.org/myroom/xxxxxxxxxxxxxxxx + + 위 방에서 채팅을 확인하실 수 있으며, 결제하신 분께 해당 금액을 정산해주시기를 부탁드립니다. + 미정산이 반복되는 경우 Taxi 서비스 이용이 제한될 수 있음을 알려드립니다. + 문의가 필요하신 경우, 서비스 내부의 "채널톡 문의하기" 혹은 메일 회신 주시면 됩니다. + + 감사합니다. + SPARCS Taxi 팀 드림. +
+
+ -
diff --git a/src/views/email.js b/src/views/email.js index acb64b7c..c26dd872 100644 --- a/src/views/email.js +++ b/src/views/email.js @@ -1,23 +1,3 @@ -const styleTitle: CSS = { - display: "flex", - alignItems: "center", - ...theme.font14, - color: theme.gray_text, - whiteSpace: "nowrap", - marginTop: "10px", -}; -const styleNickname: CSS = { - width: "100%", - ...theme.font14, - border: "none", - outline: "none", - borderRadius: "6px", - padding: "6px 12px", - marginLeft: "10px", - background: theme.purple_light, - boxShadow: theme.shadow_purple_input_inset, -}; - const emailView = (title, contents) => ` @@ -41,14 +76,18 @@
미정산 내역 관리 안내
-
- OOO (까다로운 기초항공프로젝트_OOOOO) 님께 - +
+
OOO (까다로운 기초항공프로젝트_OOOOO)
+
님께
+
+
안녕하세요, OOO (까다로운 기초항공프로젝트_OOOOO) 님. +
KAIST 학부 총학생회 산하 특별기구 SPARCS의 Taxi 팀입니다. - - 최근 참여하신 방에서 정산이 이루어지지 않았다는 사용자의 문의가 접수되어 메일을 보내드립니다. - +

+

+ 최근 참여하신 방에서 정산이 이루어지지 않았다는 사용자의 문의가 접수되어 메일을 보내드립니다. +

방 제목: 환경을 생각하는 동승 결제자: 깜찍한 제조 프로세스 혁신_OOOOO 링크: https://taxi.sparcs.org/myroom/xxxxxxxxxxxxxxxx From 857d7436ecbf459f7b80d74d469f31265988110a Mon Sep 17 00:00:00 2001 From: chlehdwon Date: Wed, 19 Jul 2023 14:46:48 +0000 Subject: [PATCH 267/308] Fix: add roomId in transformChatsForRoom --- src/modules/socket.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/socket.js b/src/modules/socket.js index 8a556dc9..f00d2fa0 100644 --- a/src/modules/socket.js +++ b/src/modules/socket.js @@ -42,6 +42,7 @@ const transformChatsForRoom = async (chats) => { ); } chatsToSend.push({ + roomId: chat.roomId, type: chat.type, authorId: chat.authorId._id, authorName: chat.authorId.nickname, @@ -52,6 +53,7 @@ const transformChatsForRoom = async (chats) => { inOutNames: chat.inOutNames, }); } + return chatsToSend; }; From 2bf2fb01b141d7d72449f1851e1c02a788a1ffa9 Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Fri, 21 Jul 2023 03:01:25 +0900 Subject: [PATCH 268/308] Fix: css in views/emailPage --- src/views/email copy.html | 109 ------------------ src/views/email.js | 12 -- src/views/emailNoSettlementPage.js | 12 +- .../{emailReportPage.js => emailPage.js} | 24 ++-- 4 files changed, 18 insertions(+), 139 deletions(-) delete mode 100644 src/views/email copy.html delete mode 100644 src/views/email.js rename src/views/{emailReportPage.js => emailPage.js} (85%) diff --git a/src/views/email copy.html b/src/views/email copy.html deleted file mode 100644 index cb47ae2e..00000000 --- a/src/views/email copy.html +++ /dev/null @@ -1,109 +0,0 @@ - -
-
-
- -
-
-
미정산 내역 관리 안내
-
-
OOO (까다로운 기초항공프로젝트_OOOOO)
-
님께
-
-
- 안녕하세요, OOO (까다로운 기초항공프로젝트_OOOOO) 님. -
- KAIST 학부 총학생회 산하 특별기구 SPARCS의 Taxi 팀입니다. -

-

- 최근 참여하신 방에서 정산이 이루어지지 않았다는 사용자의 문의가 접수되어 메일을 보내드립니다. -

- 방 제목: 환경을 생각하는 동승 - 결제자: 깜찍한 제조 프로세스 혁신_OOOOO - 링크: https://taxi.sparcs.org/myroom/xxxxxxxxxxxxxxxx - - 위 방에서 채팅을 확인하실 수 있으며, 결제하신 분께 해당 금액을 정산해주시기를 부탁드립니다. - 미정산이 반복되는 경우 Taxi 서비스 이용이 제한될 수 있음을 알려드립니다. - 문의가 필요하신 경우, 서비스 내부의 "채널톡 문의하기" 혹은 메일 회신 주시면 됩니다. - - 감사합니다. - SPARCS Taxi 팀 드림. -
-
- -
-
diff --git a/src/views/email.js b/src/views/email.js deleted file mode 100644 index c26dd872..00000000 --- a/src/views/email.js +++ /dev/null @@ -1,12 +0,0 @@ -const emailView = (title, contents) => ` - -
-
- 제목제목제목제목 -
-
-`; diff --git a/src/views/emailNoSettlementPage.js b/src/views/emailNoSettlementPage.js index f58599c1..49bb0ceb 100644 --- a/src/views/emailNoSettlementPage.js +++ b/src/views/emailNoSettlementPage.js @@ -1,24 +1,24 @@ const { frontUrl } = require("../../loadenv"); -const emailReportPage = require("./emailReportPage"); +const emailPage = require("./emailPage"); module.exports = (name, nickname, roomName, payer, roomId) => - emailReportPage( + emailPage( "미정산 내역 관련 안내", `${name} (${nickname}) 님께

안녕하세요, ${name} (${nickname}) 님.
KAIST 학부 총학생회 산하 특별기구 SPARCS의 Taxi 팀입니다.

최근 참여하신 방에서 정산이 이루어지지 않았다는 사용자의 문의가 접수되어 메일을 보내드립니다.

-