diff --git a/.idea/transcendence.iml b/.idea/transcendence.iml
index 24643cc3..260d9929 100644
--- a/.idea/transcendence.iml
+++ b/.idea/transcendence.iml
@@ -8,5 +8,6 @@
+
\ No newline at end of file
diff --git a/frontend/assets/css/common/header.css b/frontend/assets/css/common/header.css
index b7a32344..942eadba 100644
--- a/frontend/assets/css/common/header.css
+++ b/frontend/assets/css/common/header.css
@@ -19,11 +19,70 @@ header {
width: fit-content;
}
+.header-wrapper .histories#left-side, .header-wrapper .histories#right-side {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+ width: 30%;
+ height: 100%;
+}
+
+.header-wrapper .histories#left-side {
+ justify-content: flex-start;
+}
+
+.header-wrapper .histories#right-side {
+ justify-content: flex-end;
+}
+
+.header-wrapper .histories#left-side > img {
+ width: 3rem;
+ height: 3rem;
+ margin-right: 10px;
+}
+
+.header-wrapper .histories#search-box {
+ display: flex;
+ width: fit-content;
+ height: 100%;
+ justify-content: center;
+ align-items: center;
+}
+
+.header-wrapper .histories#search-box > input {
+ width: 15rem;
+ height: 50%;
+ border-radius: 1rem;
+ border: 4px #FF79C5 solid;
+ background: none;
+ color: white;
+ font-size: 1.5rem;
+ padding: 0 1rem;
+ font-family: Galmuri11, system-ui;
+}
+
.header-wrapper .histories#title {
+ text-align: center;
color: #29ABE2;
}
-.header-wrapper .histories#close-button a {
+.header-wrapper .histories#user-avatar {
+ position: relative;
+ width: 4rem;
+ height: 4rem;
+ border: 4px solid white;
+ overflow: hidden;
+ margin-right: 5rem;
+}
+
+.header-wrapper .histories#user-avatar > img {
+ position: absolute;
+ width: 6rem;
+ height: 6rem;
+ border-radius: 50%;
+}
+
+.header-wrapper .histories#close-button {
display: block;
background-image: url("../../images/close.png");
background-position: center;
diff --git a/frontend/assets/css/histories.css b/frontend/assets/css/histories.css
new file mode 100644
index 00000000..bc835500
--- /dev/null
+++ b/frontend/assets/css/histories.css
@@ -0,0 +1,150 @@
+.histories#content-wrapper {
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ height: 100%;
+ background-color: black;
+ background-image: url("../images/histories_frame.png");
+ background-size: 100% 100%;
+ background-position: bottom;
+ background-repeat: no-repeat;
+ overflow: hidden;
+}
+
+/************* Histories List *************/
+
+.histories#list {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 66.6vh;
+ color: white;
+ padding: 10vh 6vw 3vh 6vw;
+}
+
+.histories#list-wrapper {
+ display:grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-template-rows: repeat(2, 1fr);
+ justify-content: center;
+ justify-items: center;
+ align-items: center;
+ grid-gap: 1vh;
+ width: 80%;
+ height: 100%;
+}
+
+.histories.list-item {
+ display: flex;
+ justify-content: center;
+ align-items: baseline;
+ flex-direction: row;
+ border: 3px transparent solid;
+ border-radius: 1vw;
+ text-decoration: none;
+ width: 80%;
+ padding: 1vh 1vw;
+}
+
+.histories.list-item:visited, .histories.list-item:link, .histories.list-item:active {
+ color: white;
+}
+
+.histories.list-item .player {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ width: 10vw;
+ border-radius: 1vh;
+}
+
+.histories.list-item .player > div:not(:first-child) {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 50%;
+}
+
+.histories.list-item .histories.avatar {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ width: 8vw;
+ height: 8vw;
+ overflow: hidden;
+}
+
+.histories.list-item .histories.avatar > img {
+ position: absolute;
+ top: 0.1vw;
+ width: 12vw;
+ height: 12vw;
+}
+
+.histories.list-item .player > div:not(:last-child) {
+ margin-bottom: 1vh;
+}
+
+.histories.list-item .game-mode {
+ width: 20%;
+}
+
+.histories.list-item .histories.nickname {
+ font-family: Galmuri11-Bold, serif;
+ font-size: 1.3em;
+}
+
+.histories.list-item .histories.rating {
+ font-family: Galmuri11, serif;
+ font-size: 1.1em;
+ text-align: center;
+}
+
+/********** Histories Pagination **********/
+
+.histories#pagination, .histories#pagination div {
+ display: inline-block;
+ height: 6vh;
+ text-align: center;
+}
+
+.histories#pagination > .histories img {
+ height: 100%;
+ margin: 0 0.5vw;
+}
+
+.histories#pagination > .histories#prev img {
+ transform: rotate(180deg);
+}
+
+.histories#pagination#next {
+}
+
+/********** Histories Mode Select **********/
+
+.histories#mode {
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ flex-direction: row;
+ height: 15.5vh;
+ font-size: 2em;
+}
+
+.histories#mode div {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: white;
+ font-family: Galmuri11, serif;
+ text-decoration: none;
+}
+
+.histories#mode > div img {
+ width: 7vh;
+ margin-right: 2vh;
+}
diff --git a/frontend/assets/css/histories/one_on_one.css b/frontend/assets/css/histories/one_on_one.css
deleted file mode 100644
index 0696a7b9..00000000
--- a/frontend/assets/css/histories/one_on_one.css
+++ /dev/null
@@ -1,12 +0,0 @@
-.histories#content-wrapper {
- display: flex;
- justify-content: center;
- flex-direction: column;
- height: 100%;
- background-color: black;
- background-image: url("../../images/histories_frame.png");
- background-size: 100% 100%;
- background-position: bottom;
- background-repeat: no-repeat;
- overflow: hidden;
-}
diff --git a/frontend/assets/images/avatar/blue.png b/frontend/assets/images/avatar/blue.png
new file mode 100644
index 00000000..686dd3f5
Binary files /dev/null and b/frontend/assets/images/avatar/blue.png differ
diff --git a/frontend/assets/images/avatar/green.png b/frontend/assets/images/avatar/green.png
new file mode 100644
index 00000000..f375ecdf
Binary files /dev/null and b/frontend/assets/images/avatar/green.png differ
diff --git a/frontend/assets/images/avatar/pink.png b/frontend/assets/images/avatar/pink.png
new file mode 100644
index 00000000..a9484eb8
Binary files /dev/null and b/frontend/assets/images/avatar/pink.png differ
diff --git a/frontend/assets/images/avatar/red.png b/frontend/assets/images/avatar/red.png
new file mode 100644
index 00000000..fc19a984
Binary files /dev/null and b/frontend/assets/images/avatar/red.png differ
diff --git a/frontend/assets/images/avatar/yellow.png b/frontend/assets/images/avatar/yellow.png
new file mode 100644
index 00000000..e58041f3
Binary files /dev/null and b/frontend/assets/images/avatar/yellow.png differ
diff --git a/frontend/assets/images/custom_summary.png b/frontend/assets/images/custom_summary.png
new file mode 100644
index 00000000..0d4bc781
Binary files /dev/null and b/frontend/assets/images/custom_summary.png differ
diff --git a/frontend/assets/images/pagination.png b/frontend/assets/images/pagination.png
new file mode 100644
index 00000000..f96bb744
Binary files /dev/null and b/frontend/assets/images/pagination.png differ
diff --git a/frontend/assets/images/search.png b/frontend/assets/images/search.png
new file mode 100644
index 00000000..24b88a2e
Binary files /dev/null and b/frontend/assets/images/search.png differ
diff --git a/frontend/assets/images/setting.png b/frontend/assets/images/setting.png
new file mode 100644
index 00000000..a08db117
Binary files /dev/null and b/frontend/assets/images/setting.png differ
diff --git a/frontend/index.html b/frontend/index.html
index f4cf5b29..e4f40278 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1,12 +1,13 @@
- d
+
+
+
+
+
+
![summary](../../../assets/images/custom_summary.png)
+ 개요
+
+
+
![custom-mode](../../../assets/images/setting.png)
+ 사용자 지정 모드
+
+
+
![tournament](../../../assets/images/tournament_logo.png)
+ 토너먼트 모드
+
+
+
+ `;
+ }
+
+ /**
+ * 사용자 지정 모드의 전적 리스트를 렌더링합니다.
+ * 1. 플레이어 1의 정보를 렌더링합니다.
+ * 2. 게임 모드(1 vs 1 로고 또는 토너먼트 로고)를 렌더링합니다.
+ * 3. 플레이어 2의 정보를 렌더링합니다.
+ */
+ this.renderList = () => {
+ let list = document.getElementById("list");
+ list.innerHTML = `
+
+ `
+ let listWrapper = document.getElementById("list-wrapper");
+ listWrapper.innerHTML = '';
+ let mockData = [
+ {
+ player1: {
+ nickname: "hyojocho",
+ avatar: "../../../assets/images/avatar/red.png",
+ rating: 2130,
+ is_winner: true,
+ },
+ player2: {
+ nickname: "yena",
+ avatar: "../../../assets/images/avatar/blue.png",
+ rating: 110,
+ is_winner: false,
+ },
+ },
+ {
+ player1: {
+ nickname: "hyojocho",
+ avatar: "../../../assets/images/avatar/red.png",
+ rating: 2130,
+ is_winner: true,
+ },
+ player2: {
+ nickname: "yena",
+ avatar: "../../../assets/images/avatar/blue.png",
+ rating: 110,
+ is_winner: false,
+ },
+ },
+ {
+ player1: {
+ nickname: "hyojocho",
+ avatar: "../../../assets/images/avatar/red.png",
+ rating: 2130,
+ is_winner: true,
+ },
+ player2: {
+ nickname: "yena",
+ avatar: "../../../assets/images/avatar/blue.png",
+ rating: 110,
+ is_winner: false,
+ },
+ },
+ {
+ player1: {
+ nickname: "hyojocho",
+ avatar: "../../../assets/images/avatar/red.png",
+ rating: 2130,
+ is_winner: true,
+ },
+ player2: {
+ nickname: "donghyk2",
+ avatar: "../../../assets/images/avatar/green.png",
+ rating: 2120,
+ is_winner: false,
+ },
+ },
+ ]; // TODO: 백엔드로부터 데이터 받아오기
+ for (let data of mockData) {
+ const listItemDiv = document.createElement("div");
+ listItemDiv.className = "histories list-item";
+ this.renderPlayer(listItemDiv, data.player1);
+ this.renderGameMode(listItemDiv);
+ this.renderPlayer(listItemDiv, data.player2);
+ hoverChangeBorder(listItemDiv, "3px solid transparent", "3px solid #29ABE2");
+ hoverChangeCursor(listItemDiv, "pointer");
+ listItemDiv.addEventListener("click", () => {
+ console.log("TODO => 전적 상세 페이지로 이동")
+ });
+ listWrapper.appendChild(listItemDiv);
+ }
+ }
+
+ /**
+ * 전적 리스트의 플레이어 정보를 렌더링합니다.
+ * @param listItemDiv 전적 리스트의 플레이어 정보를 렌더링할 리스트 아이템
엘리먼트
+ * @param data 전적 리스트의 플레이어 정보
+ */
+ this.renderPlayer = (listItemDiv, data) => {
+ const playerDiv = document.createElement("div");
+ playerDiv.className = "histories player";
+ playerDiv.innerHTML = `
+
+
+
${data.nickname}
+
Rating: ${data.rating}
`;
+ listItemDiv.appendChild(playerDiv);
+ }
+
+ /**
+ * 전적 리스트의 게임 모드(1 vs 1 로고 또는 토너먼트 로고)를 렌더링합니다.
+ * @param listItemDiv 전적 리스트의 게임 모드를 렌더링할 리스트 아이템
엘리먼트
+ */
+ this.renderGameMode = (listItemDiv) => {
+ const gameModeDiv = document.createElement("div");
+ gameModeDiv.className = "histories game-mode";
+ gameModeDiv.innerHTML = `
+
![1v1](../../../assets/images/1vs1_logo.png)
+ `;
+ listItemDiv.appendChild(gameModeDiv);
+ }
+
+ /**
+ * 레이아웃 엘리먼트에 이벤트 리스너를 추가합니다.
+ */
+ this.addEventListenersToLayout = () => {
+ const summary = document.getElementById("summary");
+ const custom = document.getElementById("custom");
+ const tournament = document.getElementById("tournament");
+ const prev = document.getElementById("prev");
+ const next = document.getElementById("next");
+
+ // 폰트 색상 변경
+ hoverChangeColor(summary, "#ffffff", "#29ABE2");
+ hoverChangeColor(custom, "#ffffff", "#29ABE2");
+ hoverChangeColor(tournament, "#ffffff", "#29ABE2");
+
+ // 폰트 변경
+ hoverChangeFont(summary, "Galmuri11, serif", "Galmuri11-Bold, serif");
+ hoverChangeFont(custom, "Galmuri11, serif", "Galmuri11-Bold, serif");
+ hoverChangeFont(tournament, "Galmuri11, serif", "Galmuri11-Bold, serif");
+
+ // 커서 변경
+ hoverChangeCursor(summary, "pointer");
+ hoverChangeCursor(custom, "pointer");
+ hoverChangeCursor(tournament, "pointer");
+ hoverChangeCursor(prev, "pointer");
+ hoverChangeCursor(next, "pointer");
+
+ // click 이벤트
+ click(summary, function () {console.log("TODO => 개요 페이지로 이동")});
+ click(custom, function () {console.log("TODO => 사용자 지정 모드 페이지로 이동")});
+ click(tournament, function () {console.log("TODO => 토너먼트 모드 페이지로 이동")});
+ click(prev, function () {console.log("TODO => 이전 페이지로 이동")});
+ click(next, function () {console.log("TODO => 다음 페이지로 이동")});
}
this.render();
-}
\ No newline at end of file
+ this.addEventListenersToLayout();
+}
diff --git a/frontend/src/utils/clickEvent.js b/frontend/src/utils/clickEvent.js
new file mode 100644
index 00000000..088a2c59
--- /dev/null
+++ b/frontend/src/utils/clickEvent.js
@@ -0,0 +1,4 @@
+// TODO => click 시의 동작은 여기서 정의
+export const click = (element, func) => {
+ element.addEventListener("click", func);
+}
diff --git a/frontend/src/utils/hoverEvent.js b/frontend/src/utils/hoverEvent.js
new file mode 100644
index 00000000..5366dec4
--- /dev/null
+++ b/frontend/src/utils/hoverEvent.js
@@ -0,0 +1,58 @@
+/**
+ * 마우스 hover 시 폰트 색상 변경
+ * @param element hover 효과를 넣고 싶은 html 엘리먼트
+ * @param originalColor 원래 색상
+ * @param hoverColor hover 시 색상
+ */
+export const hoverChangeColor = (element, originalColor, hoverColor) => {
+ element.addEventListener("mouseover", () => {
+ element.style.color = hoverColor;
+ });
+ element.addEventListener("mouseout", () => {
+ element.style.color = originalColor;
+ });
+}
+
+/**
+ * 마우스 hover 시 폰트 변경
+ * @param element hover 효과를 넣고 싶은 html 엘리먼트
+ * @param originalFont 원래 폰트
+ * @param hoverFont hover 시 폰트
+ */
+export const hoverChangeFont = (element, originalFont, hoverFont) => {
+ element.addEventListener("mouseover", () => {
+ element.style.fontFamily = hoverFont;
+ });
+ element.addEventListener("mouseout", () => {
+ element.style.fontFamily = originalFont;
+ });
+}
+
+/**
+ * 마우스 hover 시 border 변경
+ * @param element hover 효과를 넣고 싶은 html 엘리먼트
+ * @param originalBorder 원래 border
+ * @param hoverBorder hover 시 border
+ */
+export const hoverChangeBorder = (element, originalBorder, hoverBorder) => {
+ element.addEventListener("mouseover", () => {
+ element.style.border = hoverBorder;
+ });
+ element.addEventListener("mouseout", () => {
+ element.style.border = originalBorder;
+ });
+}
+
+/**
+ * 마우스 hover 시 cursor 변경
+ * @param element hover 효과를 넣고 싶은 html 엘리먼트
+ * @param cursor hover 시 cursor
+ */
+export const hoverChangeCursor = (element, cursor) => {
+ element.addEventListener("mouseover", () => {
+ element.style.cursor = cursor;
+ });
+ element.addEventListener("mouseout", () => {
+ element.style.cursor = "default";
+ });
+}
diff --git a/nginx/config/owasp-crs/CHANGES.md b/nginx/config/owasp-crs/CHANGES.md
index 9ad99c47..700bdb4c 100644
--- a/nginx/config/owasp-crs/CHANGES.md
+++ b/nginx/config/owasp-crs/CHANGES.md
@@ -562,7 +562,7 @@ Fixes and improvements:
* Avoid embedded anchors in CRS rule 942330 (Allan Boll)
* Update 942450 for less false positives, more tests (#1662) (Will Woodson)
* Ensure single ranges are also checked (#1661) (Federico G. Schwindt)
- * WordPress: also exclude posts/pages endpoint in subdirectories (Walter Hop)
+ * WordPress: also exclude posts/page endpoint in subdirectories (Walter Hop)
* For bugs, also ask for the environment (#1657) (Federico G. Schwindt)
* XenForo: fix incorrect escape (Walter Hop)
* XenForo: additional exclusions (Walter Hop)
@@ -761,7 +761,7 @@ Fixes and improvements:
* Update RESPONSE-950-DATA-LEAKAGES.conf (Christoph Hansen)
* Update RESPONSE-959-BLOCKING-EVALUATION.conf (Christoph Hansen)
* Wordpress: add support for Gutenberg editor (siric_, Walter Hop)
- * Wordpress: allow searching for any term in admin posts/pages overview (Walter Hop)
+ * Wordpress: allow searching for any term in admin posts/page overview (Walter Hop)
* WordPress: exclude Gutenberg via rest_route (Walter Hop)
* WordPress: exclude some more profile.php fields from RFI rule (Walter Hop)
* WordPress: exclude SQL comment rule from _wp_http_referer (Walter Hop)
diff --git a/nginx/config/owasp-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf b/nginx/config/owasp-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
index 5861bf5a..5ea74544 100644
--- a/nginx/config/owasp-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
+++ b/nginx/config/owasp-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
@@ -383,7 +383,7 @@ SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 3" "id:921016,phase:2,pass,nolog,skipAf
# Forbid Request Range Header
#
-# It is possible abuse the HTTP Request Range Header to leak error pages
+# It is possible abuse the HTTP Request Range Header to leak error page
# and other information in very small snippets.
# The easiest way to fight this is to deny the use of this header.
# This is a viable option since the header is only used in rare circumstances
diff --git a/nginx/config/owasp-crs/rules/iis-errors.data b/nginx/config/owasp-crs/rules/iis-errors.data
index 4449d6ac..f1905589 100644
--- a/nginx/config/owasp-crs/rules/iis-errors.data
+++ b/nginx/config/owasp-crs/rules/iis-errors.data
@@ -1,4 +1,4 @@
-# This list comes from the default IIS error pages
+# This list comes from the default IIS error page
# To renerate get the files from a default installation and use:
# grep -h '