From b80c96ff0281a9b45b203ccdf4956dfd905a7435 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sun, 20 Oct 2024 13:16:48 +0300 Subject: [PATCH 001/117] reporter_worker: initial commit --- REPORTER_WORKER/app.py | 62 ++++ REPORTER_WORKER/csv_export_from_db.py | 34 ++ REPORTER_WORKER/filename.csv | 71 ++++ REPORTER_WORKER/generate_report.py | 15 + REPORTER_WORKER/jwt_gen.py | 18 + .../_example_/20241011_115432.pdf | Bin 0 -> 19264 bytes .../localstorage/_example_/filename.csv | 71 ++++ .../localstorage/_example_/gen_report.py | 327 ++++++++++++++++++ REPORTER_WORKER/pipelines/gen_report.py | 327 ++++++++++++++++++ REPORTER_WORKER/requirements.txt | 8 + 10 files changed, 933 insertions(+) create mode 100644 REPORTER_WORKER/app.py create mode 100644 REPORTER_WORKER/csv_export_from_db.py create mode 100644 REPORTER_WORKER/filename.csv create mode 100644 REPORTER_WORKER/generate_report.py create mode 100644 REPORTER_WORKER/jwt_gen.py create mode 100644 REPORTER_WORKER/localstorage/_example_/20241011_115432.pdf create mode 100644 REPORTER_WORKER/localstorage/_example_/filename.csv create mode 100644 REPORTER_WORKER/localstorage/_example_/gen_report.py create mode 100644 REPORTER_WORKER/pipelines/gen_report.py create mode 100644 REPORTER_WORKER/requirements.txt diff --git a/REPORTER_WORKER/app.py b/REPORTER_WORKER/app.py new file mode 100644 index 0000000..f295933 --- /dev/null +++ b/REPORTER_WORKER/app.py @@ -0,0 +1,62 @@ +import pathlib +from datetime import datetime + +import jwt +from flask import Flask, send_file, request +from werkzeug.utils import secure_filename + +app = Flask(__name__) + + +@app.route("/storage") +def sendfile(): + token = request.values["token"] + + decoded = jwt.decode(token, "secret", algorithms=["HS256"]) + secure_path = secure_filename(decoded["reportID"]) + "/" + secure_filename(decoded["filename"]) + + decoded_expired_time = datetime.fromtimestamp(decoded["valid"]) + + if datetime.now() > decoded_expired_time: + print(datetime.now(), ">", decoded_expired_time) + print() + + return "Not found", 404 + else: + print(datetime.now(), "<", decoded_expired_time) + print(token) + print(secure_path) + return send_file(f"localstorage/{secure_path}", as_attachment=True) + + +@app.route("/storage/list") +def listoffiles(): + token = request.values["reportid"] + + # decoded = jwt.decode(token, "secret", algorithms=["HS256"]) + # secure_path = secure_filename(decoded["reportID"]) + "/" + secure_filename(decoded["filename"]) + + # decoded_expired_time = datetime.fromtimestamp(decoded["valid"]) + + # if datetime.now() > decoded_expired_time: + # print(datetime.now(), ">", decoded_expired_time) + # print() + # + # return "Not found", 404 + # else: + # print(datetime.now(), "<", decoded_expired_time) + # print(token) + # print(secure_path) + # return send_file(f"localstorage/{secure_path}", as_attachment=True) + from os import listdir + from os.path import isfile, join + + mypath = f"localstorage/{secure_filename(token)}" + + onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))] + onlyfiles = filter(lambda filename: filename.endswith("pdf"), onlyfiles) + return list(onlyfiles) + + +if __name__ == '__main__': + app.run(debug=True) diff --git a/REPORTER_WORKER/csv_export_from_db.py b/REPORTER_WORKER/csv_export_from_db.py new file mode 100644 index 0000000..b4f9dfc --- /dev/null +++ b/REPORTER_WORKER/csv_export_from_db.py @@ -0,0 +1,34 @@ +# ============================================== +# 1. Подключаем библиотеки Python +# ============================================== +import psycopg2 +import csv + +# ============================================== +# 2. Подключаемся к базе данных PGSQL +# ============================================== +conn = psycopg2.connect(dbname='cb', user='postgres', + password='admin', host='localhost') +# ============================================== +# 3. Получаем данные, кладем их в курсор +# ============================================== +cursor = conn.cursor() +cursor.execute('select * from champsends_10') +# --- Получаем наименования колонок +column_names = [] +for row in cursor.description: + column_names.append(row[0]) +# ============================================== +# 4. Пишем файл CSV с колонками +# ============================================== +with open('filename.csv', 'w', newline='', encoding="utf-8") as filename: + write_filename = csv.writer(filename, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) + write_filename.writerow(column_names) + for row in cursor: + write_filename.writerow(row) +# ============================================== +# 5. Закрываем курсор +# Закрываем соединение с Базой данных +# ============================================== +cursor.close() +conn.close() \ No newline at end of file diff --git a/REPORTER_WORKER/filename.csv b/REPORTER_WORKER/filename.csv new file mode 100644 index 0000000..39fcfc4 --- /dev/null +++ b/REPORTER_WORKER/filename.csv @@ -0,0 +1,71 @@ +id,problem_letter,problem_name,problem_id,user_id,send_time,state,description,program,score,lang +22,A,Neon,2,2,2023-09-24 18:47:50.208808,Тестируется,,sq,100,2 +18,C,Neon,2,2,2023-09-21 15:00:15.473756,Тестируется,,python,90,2 +25,A,Neon,2,2,2023-09-24 18:51:36.057261,Тестируется,,sq,,2 +26,A,Neon,2,2,2023-10-15 13:13:31.559252,Тестируется,,"import time + +time.sleep(5000)",,1 +28,A,Neon,2,2,2023-10-18 18:27:19.409318,Тестируется,,"print(""478"")",,1 +19,C,Neon,2,2,2023-09-21 15:01:00.288296,Тестируется,,python,50,2 +21,C,Neon,2,2,2023-09-21 15:01:53.896737,Тестируется,,python,5,2 +30,B,Футбол,5,2,2023-10-26 13:08:40.708081,Тестируется,"[ + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\4/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 22 + }, + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\5/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 12 + }, + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\6/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 10 + } +]",sq,,1 +2,B,Neon,2,1,2023-09-24 18:51:05.416562,Протестировано,"[ + { + ""success"": false, + ""out"": ""Main.java:1: error: reached end of file while parsing\nsq\n^\n1 error\nerror: compilation failed"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 326 + }, + { + ""success"": false, + ""out"": ""Main.java:1: error: reached end of file while parsing\nsq\n^\n1 error\nerror: compilation failed"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 287 + } +]",sq,0,2 +31,B,Футбол,5,2,2023-11-10 17:02:11.970514,Протестировано,"[ + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\4/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 22 + }, + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\5/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 12 + }, + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\6/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 10 + } +]",05,100,1 +17,C,Neon,2,4,2023-09-21 14:59:32.451938,Тестируется,,python,0,2 +20,C,Neon,2,4,2023-09-21 15:01:27.323250,Тестируется,,python,80,2 +23,A,Neon,2,3,2023-09-24 18:50:03.296020,Тестируется,,sq,,2 +27,A,Neon,2,3,2023-10-15 13:13:33.810387,Тестируется,,"import time + +time.sleep(5000)",,1 +29,A,Neon,2,4,2023-10-26 13:07:50.535370,Тестируется,,x = 0,,1 diff --git a/REPORTER_WORKER/generate_report.py b/REPORTER_WORKER/generate_report.py new file mode 100644 index 0000000..ae8f563 --- /dev/null +++ b/REPORTER_WORKER/generate_report.py @@ -0,0 +1,15 @@ +import os +import shutil + + +path = "localstorage/2" + +os.mkdir(path) +shutil.copyfile("pipelines/gen_report.py", f"{path}/gen_report.py") +shutil.copyfile("filename.csv", f"{path}/filename.csv") + +os.chdir("localstorage/2") + +with open("gen_report.py", encoding="utf-8") as hello: + exec(hello.read()) + diff --git a/REPORTER_WORKER/jwt_gen.py b/REPORTER_WORKER/jwt_gen.py new file mode 100644 index 0000000..32af38d --- /dev/null +++ b/REPORTER_WORKER/jwt_gen.py @@ -0,0 +1,18 @@ +import jwt + +import time + +# Python3 code to illustrate the addition +# of time onto the datetime object + +# Importing datetime +import datetime + +# Initializing a date and time +date_and_time = datetime.datetime.now() +# Calling the timedelta() function +time_change = datetime.timedelta(minutes=1) +new_time = date_and_time + time_change + +encoded = jwt.encode({"reportID": "1", "filename": "background.jpg", "valid": new_time.timestamp()}, "secret", algorithm="HS256") +print(encoded) diff --git a/REPORTER_WORKER/localstorage/_example_/20241011_115432.pdf b/REPORTER_WORKER/localstorage/_example_/20241011_115432.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8bb96259ef2ac55e407848cc331691ef22c90813 GIT binary patch literal 19264 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 o02taDZ4@z94giqddI3DNGtLx?+j4=x(P-c\n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 22 + }, + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\5/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 12 + }, + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\6/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 10 + } +]",sq,,1 +2,B,Neon,2,1,2023-09-24 18:51:05.416562,Протестировано,"[ + { + ""success"": false, + ""out"": ""Main.java:1: error: reached end of file while parsing\nsq\n^\n1 error\nerror: compilation failed"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 326 + }, + { + ""success"": false, + ""out"": ""Main.java:1: error: reached end of file while parsing\nsq\n^\n1 error\nerror: compilation failed"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 287 + } +]",sq,0,2 +31,B,Футбол,5,2,2023-11-10 17:02:11.970514,Протестировано,"[ + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\4/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 22 + }, + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\5/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 12 + }, + { + ""success"": false, + ""out"": ""Traceback (most recent call last):\n File \""/adir\\6/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", + ""msg"": ""RUNTIME_ERROR"", + ""time"": 10 + } +]",05,100,1 +17,C,Neon,2,4,2023-09-21 14:59:32.451938,Тестируется,,python,0,2 +20,C,Neon,2,4,2023-09-21 15:01:27.323250,Тестируется,,python,80,2 +23,A,Neon,2,3,2023-09-24 18:50:03.296020,Тестируется,,sq,,2 +27,A,Neon,2,3,2023-10-15 13:13:33.810387,Тестируется,,"import time + +time.sleep(5000)",,1 +29,A,Neon,2,4,2023-10-26 13:07:50.535370,Тестируется,,x = 0,,1 diff --git a/REPORTER_WORKER/localstorage/_example_/gen_report.py b/REPORTER_WORKER/localstorage/_example_/gen_report.py new file mode 100644 index 0000000..d074904 --- /dev/null +++ b/REPORTER_WORKER/localstorage/_example_/gen_report.py @@ -0,0 +1,327 @@ +# -*- coding: utf-8 -*- +"""Codebattles.ipynb + +Automatically generated by Colab. + +Original file is located at + https://colab.research.google.com/drive/1zgmmvq-JoBJlsAtONzosZEQ8a334dloP +""" + +import pandas as pd +from matplotlib import pyplot as plt +import numpy as np + +all_problems = ("A","B","C", "D","E","F") + +def merge(ser): + for letter in all_problems: + if letter not in ser.index: + ser[letter] = 0 + +df = pd.read_csv("filename.csv") + +df + +xdf = df.dropna(subset=['score']) +xdf + +# df = pd.read_csv("/content/generated_cv.csv") + +df = pd.DataFrame() +df["Буква задачи"] = xdf["problem_letter"] +df["Результат в баллах"] = xdf["score"] +df["Имя"] = xdf["id"] +df["Время отправки"] = xdf["send_time"] + +df + +import matplotlib.pyplot as plt + + + +def graph1(fig,ax,callback=lambda : True): + task_counts = df['Буква задачи'].value_counts() + + # Сортировка по алфавиту + task_counts_sorted = task_counts.sort_index() + + merge(task_counts_sorted) + + print(task_counts_sorted) + + # Построение графика с отсортированной осью X + plt.figure(figsize=(10, 5)) + task_counts_sorted.plot(kind='bar', color='skyblue', ax=ax) + + ax.set_ylabel('Решения') + ax.set_title('Количество решений по задачам (сортировка по алфавиту)') + + + # Настройка графика + plt.title('Количество решений по задачам (сортировка по алфавиту)') + + plt.ylabel('Количество решений') + plt.xticks(rotation=0) + plt.grid(axis='y') + + callback() + plt.close() + + + +# figure = plt.figure(figsize=(7, 10)) +figure = plt.figure(figsize=(10, 6)) +axes = figure.subplots(1, 1) +# axes = figure.subplots(2, 1) +graph1(figure,axes) +# graph1(figure,axes[0]) +# graph1(figure,axes[1]) +plt.show() + +import matplotlib.pyplot as plt + +def graph2(fig,ax,callback=lambda : True): + fig = plt.figure() + # Подсчет количества решений по каждой задаче + task_counts = df['Буква задачи'].value_counts() + + merge(task_counts) + + # Сортировка по алфавиту + task_counts_sorted = task_counts.sort_index() + + + + # Построение графика с отсортированной осью X + plt.figure(figsize=(10, 6)) + task_counts.plot(kind='bar', color='green',ax=ax) + + # Настройка графика + plt.title('Количество решений по задачам (сортировка по количеству)') + plt.xlabel('Буква задачи') + plt.ylabel('Количество решений') + plt.xticks(rotation=0) + plt.grid(axis='y') + + + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + # plt.close() + +# graph2(plt.show) + +figure = plt.figure(figsize=(7, 10)) +# figure = plt.figure(figsize=(10, 6)) +# axes = figure.subplots(1, 1) +axes = figure.subplots(2, 2) +# graph1(figure,axes) +graph2(figure,axes[0,0]) +graph2(figure,axes[0,1]) +graph2(figure,axes[1,0]) +graph2(figure,axes[1,1]) +plt.show() + +def graph3(callback=lambda : True): + fig = plt.figure() + # Подсчет количества верных решений (>= 50 баллов) для каждой задачи + df['Верное решение'] = df['Результат в баллах'] >= 50 + correct_solutions_percentage = df.groupby('Буква задачи')['Верное решение'].mean() * 100 + + # Сортировка по алфавиту + correct_solutions_sorted = correct_solutions_percentage.sort_index() + merge(correct_solutions_sorted) + + # Построение графика с процентом верных решений + plt.figure(figsize=(10, 6)) + correct_solutions_sorted.plot(kind='bar', color='lightgreen') + + print(correct_solutions_sorted) + + + # Настройка графика + plt.title('Процент верных решений по задачам (>= 50 баллов)') + plt.xlabel('Буква задачи') + plt.ylabel('Процент верных решений (%)') + plt.xticks(rotation=0) + plt.grid(axis='y') + + # Показ графика + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + plt.close() + +graph3(plt.show) + +def graph3_1(callback=lambda : True): + fig = plt.figure() + # Подсчет количества верных решений (>= 50 баллов) для каждой задачи + df['Верное решение'] = df['Результат в баллах'] >= 50 + correct_solutions_count = df.groupby('Буква задачи')['Верное решение'].sum() + + # Сортировка по алфавиту + correct_solutions_sorted = correct_solutions_count.sort_index() + merge(correct_solutions_sorted) + + # Построение графика с процентом верных решений + plt.figure(figsize=(10, 6)) + correct_solutions_sorted.plot(kind='bar', color='lightgreen') + + print(correct_solutions_sorted) + + + # Настройка графика + plt.title('Количество верных решений (>= 50 баллов)') + plt.xlabel('Буква задачи') + plt.ylabel('Количество человек, решивших задчу') + plt.xticks(rotation=0) + plt.grid(axis='y') + + # Показ графика + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + plt.close() + +graph3_1(plt.show) + +def graph4(callback=lambda : True): + import matplotlib.pyplot as plt + + correct_solutions = df[df["Результат в баллах"] >= 50].shape[0] + incorrect_solutions = df[df["Результат в баллах"] < 50].shape[0] + # Данные: количество правильных и неправильных решений + # correct_solutions = 10 # например, 70 правильных решений + # incorrect_solutions = 30 # например, 30 неправильных решений + + # Создаем список данных для диаграммы + data = [correct_solutions, incorrect_solutions] + labels = ['Правильные решения', 'Неправильные решения'] + colors = ['#4CAF50', '#F44336'] # зеленый для правильных, красный для неправильных + + # Построение круговой диаграммы + plt.figure(figsize=(10,6)) + plt.pie(data, labels=labels, autopct='%1.1f%%', startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}) + + # Настройка диаграммы + plt.title('Процент правильных и неправильных решений') + # plt.axis('equal') # чтобы круг был кругом, а не овалом + + # Показ диаграммы + # Показ графика + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + plt.close() + +graph4(plt.show) + +df['Время отправки'] = pd.to_datetime(df['Время отправки']) + +import matplotlib.pyplot as plt + +def titlegen(callback=lambda : True): + import matplotlib.pyplot as plt + + fig, ax = plt.subplots(figsize=(10, 6)) + + # Убираем оси + ax.axis('off') + + champ_name = "10Г урок 13.09" + + # Добавляем текст на график + ax.text(0.5, 0.7, 'Codebattles отчет', fontsize=25, ha='center', va='center') + ax.text(0.5, 0.5, f'Соревнование: {champ_name}', fontsize=18, ha='center', va='center') + + ax.text(0.5, 0.4, f'Начало: 13.09.2008 15:00:00', fontsize=18, ha='center', va='center') + ax.text(0.5, 0, f'Отчет сформирован: 13.09.2008 15:00:00', fontsize=18, ha='center', va='center') + + # Показ диаграммы + # Показ графика + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + plt.close() + +titlegen(plt.show) + +# Группировка по времени отправок с точностью до часа + +df_grouped = df.groupby(df['Время отправки'].dt.floor('h')).size() + +# Построение графика +plt.figure(figsize=(10,6)) +plt.plot(df_grouped.index, df_grouped.values, marker='o', linestyle='-', color='b') + +# Настройки графика +plt.title('Количество отправок по времени') +plt.xlabel('Время отправки (с точностью до часа)') +plt.ylabel('Количество отправок') +plt.xticks(rotation=45) # Повернуть метки оси X для лучшей читаемости +plt.grid(True) + +# Показ графика +plt.tight_layout() +plt.show() + +import datetime +import matplotlib.pyplot as plt +from matplotlib.backends.backend_pdf import PdfPages + +# Получение текущей даты и времени +timestamp = datetime.datetime.now() + +# Форматирование таймстемпа без пробелов +timestamp_str = timestamp.strftime("%Y%m%d_%H%M%S") + +with PdfPages(f'{timestamp_str}.pdf') as pdf: + # titlegen(pdf.savefig) + # graph1(pdf.savefig) + # graph2(pdf.savefig) + # graph3(pdf.savefig) + # graph3_1(pdf.savefig) + # graph4(pdf.savefig) + + figure = plt.figure(figsize=(8, 12)) + # figure = plt.figure(figsize=(10, 6)) + # axes = figure.subplots(1, 1) + axes = figure.subplots(2, 2) + # graph1(figure,axes) + graph2(figure,axes[0,0]) + graph2(figure,axes[0,1]) + graph2(figure,axes[1,0]) + graph2(figure,axes[1,1]) + pdf.savefig(figure) + plt.close() + + figure2 = plt.figure(figsize=(8, 12)) + # figure = plt.figure(figsize=(10, 6)) + # axes = figure.subplots(1, 1) + axes = figure2.subplots(2, 1) + # graph1(figure,axes) + graph1(figure2,axes[0]) + graph1(figure2,axes[1]) + # graph1(figure,axes[1,0]) + # graph1(figure,axes[1,1]) + pdf.savefig(figure2) + plt.close() + + # figure.close() + # plt.show() \ No newline at end of file diff --git a/REPORTER_WORKER/pipelines/gen_report.py b/REPORTER_WORKER/pipelines/gen_report.py new file mode 100644 index 0000000..d074904 --- /dev/null +++ b/REPORTER_WORKER/pipelines/gen_report.py @@ -0,0 +1,327 @@ +# -*- coding: utf-8 -*- +"""Codebattles.ipynb + +Automatically generated by Colab. + +Original file is located at + https://colab.research.google.com/drive/1zgmmvq-JoBJlsAtONzosZEQ8a334dloP +""" + +import pandas as pd +from matplotlib import pyplot as plt +import numpy as np + +all_problems = ("A","B","C", "D","E","F") + +def merge(ser): + for letter in all_problems: + if letter not in ser.index: + ser[letter] = 0 + +df = pd.read_csv("filename.csv") + +df + +xdf = df.dropna(subset=['score']) +xdf + +# df = pd.read_csv("/content/generated_cv.csv") + +df = pd.DataFrame() +df["Буква задачи"] = xdf["problem_letter"] +df["Результат в баллах"] = xdf["score"] +df["Имя"] = xdf["id"] +df["Время отправки"] = xdf["send_time"] + +df + +import matplotlib.pyplot as plt + + + +def graph1(fig,ax,callback=lambda : True): + task_counts = df['Буква задачи'].value_counts() + + # Сортировка по алфавиту + task_counts_sorted = task_counts.sort_index() + + merge(task_counts_sorted) + + print(task_counts_sorted) + + # Построение графика с отсортированной осью X + plt.figure(figsize=(10, 5)) + task_counts_sorted.plot(kind='bar', color='skyblue', ax=ax) + + ax.set_ylabel('Решения') + ax.set_title('Количество решений по задачам (сортировка по алфавиту)') + + + # Настройка графика + plt.title('Количество решений по задачам (сортировка по алфавиту)') + + plt.ylabel('Количество решений') + plt.xticks(rotation=0) + plt.grid(axis='y') + + callback() + plt.close() + + + +# figure = plt.figure(figsize=(7, 10)) +figure = plt.figure(figsize=(10, 6)) +axes = figure.subplots(1, 1) +# axes = figure.subplots(2, 1) +graph1(figure,axes) +# graph1(figure,axes[0]) +# graph1(figure,axes[1]) +plt.show() + +import matplotlib.pyplot as plt + +def graph2(fig,ax,callback=lambda : True): + fig = plt.figure() + # Подсчет количества решений по каждой задаче + task_counts = df['Буква задачи'].value_counts() + + merge(task_counts) + + # Сортировка по алфавиту + task_counts_sorted = task_counts.sort_index() + + + + # Построение графика с отсортированной осью X + plt.figure(figsize=(10, 6)) + task_counts.plot(kind='bar', color='green',ax=ax) + + # Настройка графика + plt.title('Количество решений по задачам (сортировка по количеству)') + plt.xlabel('Буква задачи') + plt.ylabel('Количество решений') + plt.xticks(rotation=0) + plt.grid(axis='y') + + + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + # plt.close() + +# graph2(plt.show) + +figure = plt.figure(figsize=(7, 10)) +# figure = plt.figure(figsize=(10, 6)) +# axes = figure.subplots(1, 1) +axes = figure.subplots(2, 2) +# graph1(figure,axes) +graph2(figure,axes[0,0]) +graph2(figure,axes[0,1]) +graph2(figure,axes[1,0]) +graph2(figure,axes[1,1]) +plt.show() + +def graph3(callback=lambda : True): + fig = plt.figure() + # Подсчет количества верных решений (>= 50 баллов) для каждой задачи + df['Верное решение'] = df['Результат в баллах'] >= 50 + correct_solutions_percentage = df.groupby('Буква задачи')['Верное решение'].mean() * 100 + + # Сортировка по алфавиту + correct_solutions_sorted = correct_solutions_percentage.sort_index() + merge(correct_solutions_sorted) + + # Построение графика с процентом верных решений + plt.figure(figsize=(10, 6)) + correct_solutions_sorted.plot(kind='bar', color='lightgreen') + + print(correct_solutions_sorted) + + + # Настройка графика + plt.title('Процент верных решений по задачам (>= 50 баллов)') + plt.xlabel('Буква задачи') + plt.ylabel('Процент верных решений (%)') + plt.xticks(rotation=0) + plt.grid(axis='y') + + # Показ графика + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + plt.close() + +graph3(plt.show) + +def graph3_1(callback=lambda : True): + fig = plt.figure() + # Подсчет количества верных решений (>= 50 баллов) для каждой задачи + df['Верное решение'] = df['Результат в баллах'] >= 50 + correct_solutions_count = df.groupby('Буква задачи')['Верное решение'].sum() + + # Сортировка по алфавиту + correct_solutions_sorted = correct_solutions_count.sort_index() + merge(correct_solutions_sorted) + + # Построение графика с процентом верных решений + plt.figure(figsize=(10, 6)) + correct_solutions_sorted.plot(kind='bar', color='lightgreen') + + print(correct_solutions_sorted) + + + # Настройка графика + plt.title('Количество верных решений (>= 50 баллов)') + plt.xlabel('Буква задачи') + plt.ylabel('Количество человек, решивших задчу') + plt.xticks(rotation=0) + plt.grid(axis='y') + + # Показ графика + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + plt.close() + +graph3_1(plt.show) + +def graph4(callback=lambda : True): + import matplotlib.pyplot as plt + + correct_solutions = df[df["Результат в баллах"] >= 50].shape[0] + incorrect_solutions = df[df["Результат в баллах"] < 50].shape[0] + # Данные: количество правильных и неправильных решений + # correct_solutions = 10 # например, 70 правильных решений + # incorrect_solutions = 30 # например, 30 неправильных решений + + # Создаем список данных для диаграммы + data = [correct_solutions, incorrect_solutions] + labels = ['Правильные решения', 'Неправильные решения'] + colors = ['#4CAF50', '#F44336'] # зеленый для правильных, красный для неправильных + + # Построение круговой диаграммы + plt.figure(figsize=(10,6)) + plt.pie(data, labels=labels, autopct='%1.1f%%', startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}) + + # Настройка диаграммы + plt.title('Процент правильных и неправильных решений') + # plt.axis('equal') # чтобы круг был кругом, а не овалом + + # Показ диаграммы + # Показ графика + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + plt.close() + +graph4(plt.show) + +df['Время отправки'] = pd.to_datetime(df['Время отправки']) + +import matplotlib.pyplot as plt + +def titlegen(callback=lambda : True): + import matplotlib.pyplot as plt + + fig, ax = plt.subplots(figsize=(10, 6)) + + # Убираем оси + ax.axis('off') + + champ_name = "10Г урок 13.09" + + # Добавляем текст на график + ax.text(0.5, 0.7, 'Codebattles отчет', fontsize=25, ha='center', va='center') + ax.text(0.5, 0.5, f'Соревнование: {champ_name}', fontsize=18, ha='center', va='center') + + ax.text(0.5, 0.4, f'Начало: 13.09.2008 15:00:00', fontsize=18, ha='center', va='center') + ax.text(0.5, 0, f'Отчет сформирован: 13.09.2008 15:00:00', fontsize=18, ha='center', va='center') + + # Показ диаграммы + # Показ графика + callback() + + # Показ графика + #plt.show() + # plt.close() + #return fig + plt.close() + +titlegen(plt.show) + +# Группировка по времени отправок с точностью до часа + +df_grouped = df.groupby(df['Время отправки'].dt.floor('h')).size() + +# Построение графика +plt.figure(figsize=(10,6)) +plt.plot(df_grouped.index, df_grouped.values, marker='o', linestyle='-', color='b') + +# Настройки графика +plt.title('Количество отправок по времени') +plt.xlabel('Время отправки (с точностью до часа)') +plt.ylabel('Количество отправок') +plt.xticks(rotation=45) # Повернуть метки оси X для лучшей читаемости +plt.grid(True) + +# Показ графика +plt.tight_layout() +plt.show() + +import datetime +import matplotlib.pyplot as plt +from matplotlib.backends.backend_pdf import PdfPages + +# Получение текущей даты и времени +timestamp = datetime.datetime.now() + +# Форматирование таймстемпа без пробелов +timestamp_str = timestamp.strftime("%Y%m%d_%H%M%S") + +with PdfPages(f'{timestamp_str}.pdf') as pdf: + # titlegen(pdf.savefig) + # graph1(pdf.savefig) + # graph2(pdf.savefig) + # graph3(pdf.savefig) + # graph3_1(pdf.savefig) + # graph4(pdf.savefig) + + figure = plt.figure(figsize=(8, 12)) + # figure = plt.figure(figsize=(10, 6)) + # axes = figure.subplots(1, 1) + axes = figure.subplots(2, 2) + # graph1(figure,axes) + graph2(figure,axes[0,0]) + graph2(figure,axes[0,1]) + graph2(figure,axes[1,0]) + graph2(figure,axes[1,1]) + pdf.savefig(figure) + plt.close() + + figure2 = plt.figure(figsize=(8, 12)) + # figure = plt.figure(figsize=(10, 6)) + # axes = figure.subplots(1, 1) + axes = figure2.subplots(2, 1) + # graph1(figure,axes) + graph1(figure2,axes[0]) + graph1(figure2,axes[1]) + # graph1(figure,axes[1,0]) + # graph1(figure,axes[1,1]) + pdf.savefig(figure2) + plt.close() + + # figure.close() + # plt.show() \ No newline at end of file diff --git a/REPORTER_WORKER/requirements.txt b/REPORTER_WORKER/requirements.txt new file mode 100644 index 0000000..c0f48aa --- /dev/null +++ b/REPORTER_WORKER/requirements.txt @@ -0,0 +1,8 @@ +PyJWT~=2.9.0 +Flask~=3.0.3 +Werkzeug~=3.0.4 +psycopg2~=2.9.9 + +pandas +matplotlib +numpy \ No newline at end of file From 9695555edffc613257c1294d00b90526f576b30d Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Mon, 28 Oct 2024 00:33:34 +0300 Subject: [PATCH 002/117] reporter_worker: basic structure --- BACKEND/src/database/migrations.py | 14 +- REPORTER_WORKER/.env.example | 7 + REPORTER_WORKER/.gitignore | 162 ++++++++++++++++++ REPORTER_WORKER/app.py | 16 +- REPORTER_WORKER/requirements.txt | 8 +- REPORTER_WORKER/web/controller/__init__.py | 0 .../web/controller/externalController.py | 0 .../web/controller/internalController.py | 0 .../web/service/dataBaseService.py | 41 +++++ .../web/service/databaseExportService.py | 41 +++++ REPORTER_WORKER/web/service/envService.py | 5 + REPORTER_WORKER/web/service/jwtService.py | 5 + 12 files changed, 291 insertions(+), 8 deletions(-) create mode 100644 REPORTER_WORKER/.env.example create mode 100644 REPORTER_WORKER/.gitignore create mode 100644 REPORTER_WORKER/web/controller/__init__.py create mode 100644 REPORTER_WORKER/web/controller/externalController.py create mode 100644 REPORTER_WORKER/web/controller/internalController.py create mode 100644 REPORTER_WORKER/web/service/dataBaseService.py create mode 100644 REPORTER_WORKER/web/service/databaseExportService.py create mode 100644 REPORTER_WORKER/web/service/envService.py create mode 100644 REPORTER_WORKER/web/service/jwtService.py diff --git a/BACKEND/src/database/migrations.py b/BACKEND/src/database/migrations.py index eeb73ee..db0585d 100644 --- a/BACKEND/src/database/migrations.py +++ b/BACKEND/src/database/migrations.py @@ -8,4 +8,16 @@ ADD COLUMN IF NOT EXISTS totp TEXT; """ -sql_migrations = [_0_ADD_TYPE_OF_PROBLEM, _1_ADD_TOTP_CODE] +_2_CREATE_REPORTS_CHAMPS_TABLE = """ +CREATE TABLE IF NOT EXISTS reports_champs ( + report_id SERIAL PRIMARY KEY, + champ_id INTEGER, + status INTEGER + ) +""" + +sql_migrations = [ + _0_ADD_TYPE_OF_PROBLEM, + _1_ADD_TOTP_CODE, + _2_CREATE_REPORTS_CHAMPS_TABLE, +] diff --git a/REPORTER_WORKER/.env.example b/REPORTER_WORKER/.env.example new file mode 100644 index 0000000..0504742 --- /dev/null +++ b/REPORTER_WORKER/.env.example @@ -0,0 +1,7 @@ +JWT_SECRET=jwtsecret +API_SECRET=internalapiprotection +PUBLIC_ENDPOINTS_PATH=storage +INTERNAL_ENDPOINTS_PATH=internal +DATABASE_NAME=cb +DATABASE_USER=postgres +DATABASE_PASSWORD=admin \ No newline at end of file diff --git a/REPORTER_WORKER/.gitignore b/REPORTER_WORKER/.gitignore new file mode 100644 index 0000000..efa407c --- /dev/null +++ b/REPORTER_WORKER/.gitignore @@ -0,0 +1,162 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/REPORTER_WORKER/app.py b/REPORTER_WORKER/app.py index f295933..cd4a6ae 100644 --- a/REPORTER_WORKER/app.py +++ b/REPORTER_WORKER/app.py @@ -1,14 +1,24 @@ -import pathlib +import os from datetime import datetime import jwt +from dotenv import load_dotenv from flask import Flask, send_file, request from werkzeug.utils import secure_filename +load_dotenv() + +# os.getenv(key, default =" None) + +JWT_SECRET = os.getenv("JWT_SECRET", default="jwtsecret") +API_SECRET = os.getenv("API_SECRET", default="internalapiprotection") +PUBLIC_ENDPOINTS_PATH = os.getenv("PUBLIC_ENDPOINTS_PATH", default="storage") +INTERNAL_ENDPOINTS_PATH = os.getenv("INTERNAL_ENDPOINTS_PATH", default="internal") + app = Flask(__name__) -@app.route("/storage") +@app.route(f"/{PUBLIC_ENDPOINTS_PATH}") def sendfile(): token = request.values["token"] @@ -29,7 +39,7 @@ def sendfile(): return send_file(f"localstorage/{secure_path}", as_attachment=True) -@app.route("/storage/list") +@app.route(f"/{PUBLIC_ENDPOINTS_PATH}/list") def listoffiles(): token = request.values["reportid"] diff --git a/REPORTER_WORKER/requirements.txt b/REPORTER_WORKER/requirements.txt index c0f48aa..4a58c05 100644 --- a/REPORTER_WORKER/requirements.txt +++ b/REPORTER_WORKER/requirements.txt @@ -2,7 +2,7 @@ PyJWT~=2.9.0 Flask~=3.0.3 Werkzeug~=3.0.4 psycopg2~=2.9.9 - -pandas -matplotlib -numpy \ No newline at end of file +pandas~=2.2.3 +matplotlib~=3.9.2 +numpy~=2.1.2 +python-dotenv~=1.0.1 \ No newline at end of file diff --git a/REPORTER_WORKER/web/controller/__init__.py b/REPORTER_WORKER/web/controller/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/REPORTER_WORKER/web/controller/externalController.py b/REPORTER_WORKER/web/controller/externalController.py new file mode 100644 index 0000000..e69de29 diff --git a/REPORTER_WORKER/web/controller/internalController.py b/REPORTER_WORKER/web/controller/internalController.py new file mode 100644 index 0000000..e69de29 diff --git a/REPORTER_WORKER/web/service/dataBaseService.py b/REPORTER_WORKER/web/service/dataBaseService.py new file mode 100644 index 0000000..0cb10b8 --- /dev/null +++ b/REPORTER_WORKER/web/service/dataBaseService.py @@ -0,0 +1,41 @@ +# ============================================== +# 1. Подключаем библиотеки Python +# ============================================== +import psycopg2 +import csv + + +class DataBaseService(): + pass + + +databaseService = DataBaseService() + +# ============================================== +# 2. Подключаемся к базе данных PGSQL +# ============================================== +conn = psycopg2.connect(dbname='cb', user='postgres', + password='admin', host='localhost') +# ============================================== +# 3. Получаем данные, кладем их в курсор +# ============================================== +cursor = conn.cursor() +cursor.execute('select * from champsends_10') +# --- Получаем наименования колонок +column_names = [] +for row in cursor.description: + column_names.append(row[0]) +# ============================================== +# 4. Пишем файл CSV с колонками +# ============================================== +with open('filename.csv', 'w', newline='', encoding="utf-8") as filename: + write_filename = csv.writer(filename, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) + write_filename.writerow(column_names) + for row in cursor: + write_filename.writerow(row) +# ============================================== +# 5. Закрываем курсор +# Закрываем соединение с Базой данных +# ============================================== +cursor.close() +conn.close() diff --git a/REPORTER_WORKER/web/service/databaseExportService.py b/REPORTER_WORKER/web/service/databaseExportService.py new file mode 100644 index 0000000..7df06cd --- /dev/null +++ b/REPORTER_WORKER/web/service/databaseExportService.py @@ -0,0 +1,41 @@ +# ============================================== +# 1. Подключаем библиотеки Python +# ============================================== +import psycopg2 +import csv + + +class DataBaseExportService(): + pass + + +dataBaseExportService = DataBaseExportService() + +# ============================================== +# 2. Подключаемся к базе данных PGSQL +# ============================================== +conn = psycopg2.connect(dbname='cb', user='postgres', + password='admin', host='localhost') +# ============================================== +# 3. Получаем данные, кладем их в курсор +# ============================================== +cursor = conn.cursor() +cursor.execute('select * from champsends_10') +# --- Получаем наименования колонок +column_names = [] +for row in cursor.description: + column_names.append(row[0]) +# ============================================== +# 4. Пишем файл CSV с колонками +# ============================================== +with open('filename.csv', 'w', newline='', encoding="utf-8") as filename: + write_filename = csv.writer(filename, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) + write_filename.writerow(column_names) + for row in cursor: + write_filename.writerow(row) +# ============================================== +# 5. Закрываем курсор +# Закрываем соединение с Базой данных +# ============================================== +cursor.close() +conn.close() diff --git a/REPORTER_WORKER/web/service/envService.py b/REPORTER_WORKER/web/service/envService.py new file mode 100644 index 0000000..51667dd --- /dev/null +++ b/REPORTER_WORKER/web/service/envService.py @@ -0,0 +1,5 @@ +class EnvService: + pass + + +envService = EnvService() diff --git a/REPORTER_WORKER/web/service/jwtService.py b/REPORTER_WORKER/web/service/jwtService.py new file mode 100644 index 0000000..85262c1 --- /dev/null +++ b/REPORTER_WORKER/web/service/jwtService.py @@ -0,0 +1,5 @@ +class JWTService: + pass + + +jwtService = JWTService() From bcf21f8fffc62a95f7d63cd303cf35d1c46bb4a8 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:15:53 +0300 Subject: [PATCH 003/117] reporter_worker: basic api --- REPORTER_WORKER/app.py | 71 ++---------------- .../20241011_115432.pdf | Bin .../{_example_ => example}/filename.csv | 0 .../{_example_ => example}/gen_report.py | 0 REPORTER_WORKER/web/controller/__init__.py | 2 + REPORTER_WORKER/web/controller/blueprint.py | 4 + .../web/controller/externalController.py | 29 +++++++ .../web/controller/internalController.py | 48 ++++++++++++ REPORTER_WORKER/web/service/envService.py | 15 +++- 9 files changed, 103 insertions(+), 66 deletions(-) rename REPORTER_WORKER/localstorage/{_example_ => example}/20241011_115432.pdf (100%) rename REPORTER_WORKER/localstorage/{_example_ => example}/filename.csv (100%) rename REPORTER_WORKER/localstorage/{_example_ => example}/gen_report.py (100%) create mode 100644 REPORTER_WORKER/web/controller/blueprint.py diff --git a/REPORTER_WORKER/app.py b/REPORTER_WORKER/app.py index cd4a6ae..0b76e05 100644 --- a/REPORTER_WORKER/app.py +++ b/REPORTER_WORKER/app.py @@ -1,72 +1,13 @@ -import os -from datetime import datetime +from flask import Flask -import jwt -from dotenv import load_dotenv -from flask import Flask, send_file, request -from werkzeug.utils import secure_filename - -load_dotenv() - -# os.getenv(key, default =" None) - -JWT_SECRET = os.getenv("JWT_SECRET", default="jwtsecret") -API_SECRET = os.getenv("API_SECRET", default="internalapiprotection") -PUBLIC_ENDPOINTS_PATH = os.getenv("PUBLIC_ENDPOINTS_PATH", default="storage") -INTERNAL_ENDPOINTS_PATH = os.getenv("INTERNAL_ENDPOINTS_PATH", default="internal") +from web.controller import * +from web.controller.blueprint import internal_api +from web.service.envService import envService app = Flask(__name__) - -@app.route(f"/{PUBLIC_ENDPOINTS_PATH}") -def sendfile(): - token = request.values["token"] - - decoded = jwt.decode(token, "secret", algorithms=["HS256"]) - secure_path = secure_filename(decoded["reportID"]) + "/" + secure_filename(decoded["filename"]) - - decoded_expired_time = datetime.fromtimestamp(decoded["valid"]) - - if datetime.now() > decoded_expired_time: - print(datetime.now(), ">", decoded_expired_time) - print() - - return "Not found", 404 - else: - print(datetime.now(), "<", decoded_expired_time) - print(token) - print(secure_path) - return send_file(f"localstorage/{secure_path}", as_attachment=True) - - -@app.route(f"/{PUBLIC_ENDPOINTS_PATH}/list") -def listoffiles(): - token = request.values["reportid"] - - # decoded = jwt.decode(token, "secret", algorithms=["HS256"]) - # secure_path = secure_filename(decoded["reportID"]) + "/" + secure_filename(decoded["filename"]) - - # decoded_expired_time = datetime.fromtimestamp(decoded["valid"]) - - # if datetime.now() > decoded_expired_time: - # print(datetime.now(), ">", decoded_expired_time) - # print() - # - # return "Not found", 404 - # else: - # print(datetime.now(), "<", decoded_expired_time) - # print(token) - # print(secure_path) - # return send_file(f"localstorage/{secure_path}", as_attachment=True) - from os import listdir - from os.path import isfile, join - - mypath = f"localstorage/{secure_filename(token)}" - - onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))] - onlyfiles = filter(lambda filename: filename.endswith("pdf"), onlyfiles) - return list(onlyfiles) - +app.register_blueprint(internal_api, url_prefix=f'/{envService.INTERNAL_ENDPOINTS_PATH}') +app.register_blueprint(external_api, url_prefix=f'/{envService.PUBLIC_ENDPOINTS_PATH}') if __name__ == '__main__': app.run(debug=True) diff --git a/REPORTER_WORKER/localstorage/_example_/20241011_115432.pdf b/REPORTER_WORKER/localstorage/example/20241011_115432.pdf similarity index 100% rename from REPORTER_WORKER/localstorage/_example_/20241011_115432.pdf rename to REPORTER_WORKER/localstorage/example/20241011_115432.pdf diff --git a/REPORTER_WORKER/localstorage/_example_/filename.csv b/REPORTER_WORKER/localstorage/example/filename.csv similarity index 100% rename from REPORTER_WORKER/localstorage/_example_/filename.csv rename to REPORTER_WORKER/localstorage/example/filename.csv diff --git a/REPORTER_WORKER/localstorage/_example_/gen_report.py b/REPORTER_WORKER/localstorage/example/gen_report.py similarity index 100% rename from REPORTER_WORKER/localstorage/_example_/gen_report.py rename to REPORTER_WORKER/localstorage/example/gen_report.py diff --git a/REPORTER_WORKER/web/controller/__init__.py b/REPORTER_WORKER/web/controller/__init__.py index e69de29..d6ff139 100644 --- a/REPORTER_WORKER/web/controller/__init__.py +++ b/REPORTER_WORKER/web/controller/__init__.py @@ -0,0 +1,2 @@ +from .externalController import * +from .internalController import * diff --git a/REPORTER_WORKER/web/controller/blueprint.py b/REPORTER_WORKER/web/controller/blueprint.py new file mode 100644 index 0000000..ca491ac --- /dev/null +++ b/REPORTER_WORKER/web/controller/blueprint.py @@ -0,0 +1,4 @@ +from flask import Blueprint + +internal_api = Blueprint('internal_api', __name__) +external_api = Blueprint('external_api', __name__) diff --git a/REPORTER_WORKER/web/controller/externalController.py b/REPORTER_WORKER/web/controller/externalController.py index e69de29..1e62f2f 100644 --- a/REPORTER_WORKER/web/controller/externalController.py +++ b/REPORTER_WORKER/web/controller/externalController.py @@ -0,0 +1,29 @@ +from datetime import datetime + +import jwt +from flask import send_file, request +from werkzeug.utils import secure_filename + +from web.controller.blueprint import external_api +from web.service.envService import envService + + +@external_api.route("/") +def sendfile(): + token = request.values["token"] + + decoded = jwt.decode(token, envService.JWT_SECRET, algorithms=["HS256"]) + secure_path = secure_filename(decoded["reportID"]) + "/" + secure_filename(decoded["filename"]) + + decoded_expired_time = datetime.fromtimestamp(decoded["valid"]) + + if datetime.now() > decoded_expired_time: + print(datetime.now(), ">", decoded_expired_time) + print() + + return "Not found", 404 + else: + print(datetime.now(), "<", decoded_expired_time) + print(token) + print(secure_path) + return send_file(f"localstorage/{secure_path}", as_attachment=True) \ No newline at end of file diff --git a/REPORTER_WORKER/web/controller/internalController.py b/REPORTER_WORKER/web/controller/internalController.py index e69de29..f69126c 100644 --- a/REPORTER_WORKER/web/controller/internalController.py +++ b/REPORTER_WORKER/web/controller/internalController.py @@ -0,0 +1,48 @@ +from datetime import datetime, timedelta +from os import listdir +from os.path import isfile, join + +import jwt +from flask import request, url_for +from werkzeug.utils import secure_filename + +from web.controller.blueprint import internal_api +from web.service.envService import envService + + +@internal_api.route("/request", methods=['POST']) +def request_file(): + token = request.json["token"] + filename = secure_filename(request.json["file"]) + report_id = secure_filename(request.json["reportID"]) + + if token != envService.API_SECRET: + return "Authorization failed", 401 + + date_and_time = datetime.now() + # Calling the timedelta() function + time_change = timedelta(minutes=1) + new_time = date_and_time + time_change + + encoded = jwt.encode({"reportID": report_id, "filename": filename, "valid": new_time.timestamp()}, + envService.JWT_SECRET, + algorithm="HS256") + + getfile_url = f"{url_for('external_api.sendfile')}" + + return {"token": encoded, "endpoint": getfile_url, "url": getfile_url + "?token=" + encoded} + + +@internal_api.route("/list") +def listoffiles(): + reportid = request.json["reportID"] + token = request.json["token"] + + if token != envService.API_SECRET: + return "Authorization failed", 401 + + mypath = f"localstorage/{secure_filename(reportid)}" + + onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))] + onlyfiles = filter(lambda filename: filename.endswith("pdf"), onlyfiles) + return list(onlyfiles) diff --git a/REPORTER_WORKER/web/service/envService.py b/REPORTER_WORKER/web/service/envService.py index 51667dd..9084054 100644 --- a/REPORTER_WORKER/web/service/envService.py +++ b/REPORTER_WORKER/web/service/envService.py @@ -1,5 +1,18 @@ +import os + +from dotenv import load_dotenv + +load_dotenv() + + class EnvService: - pass + JWT_SECRET = os.getenv("JWT_SECRET", default="jwtsecret") + API_SECRET = os.getenv("API_SECRET", default="internalapiprotection") + PUBLIC_ENDPOINTS_PATH = os.getenv("PUBLIC_ENDPOINTS_PATH", default="storage") + INTERNAL_ENDPOINTS_PATH = os.getenv("INTERNAL_ENDPOINTS_PATH", default="internal") + DATABASE_NAME = os.getenv("DATABASE_NAME", default="cb") + DATABASE_USER = os.getenv("DATABASE_USER", default="postgres") + DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD", default="admin") envService = EnvService() From 954e08ebd5eeeff7e8c6837bad98e6a46c7acb7b Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Thu, 26 Dec 2024 14:09:40 +0300 Subject: [PATCH 004/117] backend_2: initial commit --- BACKEND_V2/.gitattributes | 3 + BACKEND_V2/.gitignore | 40 +++ BACKEND_V2/build.gradle.kts | 49 ++++ BACKEND_V2/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43583 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + BACKEND_V2/gradlew | 252 ++++++++++++++++++ BACKEND_V2/gradlew.bat | 94 +++++++ BACKEND_V2/settings.gradle.kts | 1 + .../backend/BackendV2Application.kt | 11 + .../web/controllers/HelloWorldController.kt | 12 + .../src/main/resources/application.properties | 6 + .../backend/BackendV2ApplicationTests.kt | 13 + 12 files changed, 488 insertions(+) create mode 100644 BACKEND_V2/.gitattributes create mode 100644 BACKEND_V2/.gitignore create mode 100644 BACKEND_V2/build.gradle.kts create mode 100644 BACKEND_V2/gradle/wrapper/gradle-wrapper.jar create mode 100644 BACKEND_V2/gradle/wrapper/gradle-wrapper.properties create mode 100644 BACKEND_V2/gradlew create mode 100644 BACKEND_V2/gradlew.bat create mode 100644 BACKEND_V2/settings.gradle.kts create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/BackendV2Application.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/HelloWorldController.kt create mode 100644 BACKEND_V2/src/main/resources/application.properties create mode 100644 BACKEND_V2/src/test/kotlin/ru/codebattles/backend/BackendV2ApplicationTests.kt diff --git a/BACKEND_V2/.gitattributes b/BACKEND_V2/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/BACKEND_V2/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/BACKEND_V2/.gitignore b/BACKEND_V2/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/BACKEND_V2/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/BACKEND_V2/build.gradle.kts b/BACKEND_V2/build.gradle.kts new file mode 100644 index 0000000..17d102b --- /dev/null +++ b/BACKEND_V2/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + kotlin("jvm") version "1.9.25" + kotlin("plugin.spring") version "1.9.25" + id("org.springframework.boot") version "3.4.1" + id("io.spring.dependency-management") version "1.1.7" + kotlin("plugin.jpa") version "1.9.25" +} + +group = "ru.codebattles" +version = "0.0.1-SNAPSHOT" + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.jetbrains.kotlin:kotlin-reflect") + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + compileOnly("org.projectlombok:lombok") + runtimeOnly("org.postgresql:postgresql") + annotationProcessor("org.projectlombok:lombok") +} + +kotlin { + compilerOptions { + freeCompilerArgs.addAll("-Xjsr305=strict") + } +} + +allOpen { + annotation("jakarta.persistence.Entity") + annotation("jakarta.persistence.MappedSuperclass") + annotation("jakarta.persistence.Embeddable") +} + +tasks.withType { + useJUnitPlatform() +} diff --git a/BACKEND_V2/gradle/wrapper/gradle-wrapper.jar b/BACKEND_V2/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..a4b76b9530d66f5e68d973ea569d8e19de379189 GIT binary patch literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 0 HcmV?d00001 diff --git a/BACKEND_V2/gradle/wrapper/gradle-wrapper.properties b/BACKEND_V2/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e2847c8 --- /dev/null +++ b/BACKEND_V2/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/BACKEND_V2/gradlew b/BACKEND_V2/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/BACKEND_V2/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/BACKEND_V2/gradlew.bat b/BACKEND_V2/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/BACKEND_V2/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/BACKEND_V2/settings.gradle.kts b/BACKEND_V2/settings.gradle.kts new file mode 100644 index 0000000..2401f0a --- /dev/null +++ b/BACKEND_V2/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "backend" diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/BackendV2Application.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/BackendV2Application.kt new file mode 100644 index 0000000..72c06ca --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/BackendV2Application.kt @@ -0,0 +1,11 @@ +package ru.codebattles.backend + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication + +@SpringBootApplication +class BackendV2Application + +fun main(args: Array) { + runApplication(*args) +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/HelloWorldController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/HelloWorldController.kt new file mode 100644 index 0000000..eec26ef --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/HelloWorldController.kt @@ -0,0 +1,12 @@ +package ru.codebattles.backend.web.controllers + +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +class HelloWorldController { + @GetMapping + fun helloWorld(): String { + return "Hello, world!" + } +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/resources/application.properties b/BACKEND_V2/src/main/resources/application.properties new file mode 100644 index 0000000..d8b79c4 --- /dev/null +++ b/BACKEND_V2/src/main/resources/application.properties @@ -0,0 +1,6 @@ +spring.application.name=BACKEND_V2 +spring.datasource.url=jdbc:postgresql://localhost:5432/codebattles +spring.datasource.username=postgres +spring.datasource.password=admin +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file diff --git a/BACKEND_V2/src/test/kotlin/ru/codebattles/backend/BackendV2ApplicationTests.kt b/BACKEND_V2/src/test/kotlin/ru/codebattles/backend/BackendV2ApplicationTests.kt new file mode 100644 index 0000000..d39d41d --- /dev/null +++ b/BACKEND_V2/src/test/kotlin/ru/codebattles/backend/BackendV2ApplicationTests.kt @@ -0,0 +1,13 @@ +package ru.codebattles.backend + +import org.junit.jupiter.api.Test +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest +class BackendV2ApplicationTests { + + @Test + fun contextLoads() { + } + +} From 22a326c22241d2f2ce531986c9055e94a12a07ae Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Thu, 26 Dec 2024 18:41:26 +0300 Subject: [PATCH 005/117] backend_2: add entities --- BACKEND_V2/build.gradle.kts | 1 + .../backend/config/SecurityConfig.kt | 31 +++++++++++++ .../ru/codebattles/backend/entity/Answer.kt | 24 +++++++++++ .../codebattles/backend/entity/BaseEntity.kt | 43 +++++++++++++++++++ .../ru/codebattles/backend/entity/Checker.kt | 10 +++++ .../codebattles/backend/entity/Competition.kt | 32 ++++++++++++++ .../ru/codebattles/backend/entity/Problem.kt | 14 ++++++ .../ru/codebattles/backend/entity/User.kt | 26 +++++++++++ .../repository/CompetitionRepository.kt | 6 +++ .../backend/repository/UserRepository.kt | 11 +++++ .../backend/services/UserDetailService.kt | 15 +++++++ .../web/controllers/HelloWorldController.kt | 37 ++++++++++++++++ 12 files changed, 250 insertions(+) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Answer.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/BaseEntity.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Checker.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserDetailService.kt diff --git a/BACKEND_V2/build.gradle.kts b/BACKEND_V2/build.gradle.kts index 17d102b..d86ef63 100644 --- a/BACKEND_V2/build.gradle.kts +++ b/BACKEND_V2/build.gradle.kts @@ -22,6 +22,7 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-security") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") testImplementation("org.springframework.boot:spring-boot-starter-test") diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt new file mode 100644 index 0000000..af4b3db --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt @@ -0,0 +1,31 @@ +package ru.codebattles.backend.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.web.SecurityFilterChain + + +@Configuration +class SecurityConfig { + + @Bean + fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + http + .csrf { it.disable() } // Отключить CSRF при необходимости + .authorizeHttpRequests { + it + .requestMatchers("/test/**").permitAll() // Разрешить доступ без авторизации + .anyRequest().authenticated() // Все остальные запросы требуют авторизации + } + .formLogin { it } // Включить стандартный логин через форму + return http.build() + } + + @Bean + fun encoder(): PasswordEncoder { + return BCryptPasswordEncoder() + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Answer.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Answer.kt new file mode 100644 index 0000000..eb056f6 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Answer.kt @@ -0,0 +1,24 @@ +package ru.codebattles.backend.entity + +import jakarta.persistence.* + +enum class AnswerStatus { + IN_PROGRESS, + COMPLETED, +} + +@Entity +data class Answer( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + + @ManyToOne + val competition: Competition, + @ManyToOne + val user: User, + @Enumerated(EnumType.STRING) + val status: AnswerStatus = AnswerStatus.IN_PROGRESS, + val score: Int = 0, +) + diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/BaseEntity.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/BaseEntity.kt new file mode 100644 index 0000000..0537b72 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/BaseEntity.kt @@ -0,0 +1,43 @@ +package ru.codebattles.backend.entity + +import jakarta.persistence.* +import lombok.EqualsAndHashCode +import lombok.Getter +import lombok.Setter +import org.hibernate.annotations.CreationTimestamp +import org.hibernate.annotations.UpdateTimestamp +import java.io.Serializable +import java.util.* + +@Getter +@Setter +@MappedSuperclass +abstract class BaseEntity : Serializable { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null + + + @CreationTimestamp + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_at") + val createdAt: Date? = null + + @UpdateTimestamp + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "updated_at") + val updatedAt: Date? = null + + + override fun hashCode(): Int { + return Objects.hash(id) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as BaseEntity + return id == other.id + } +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Checker.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Checker.kt new file mode 100644 index 0000000..2492749 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Checker.kt @@ -0,0 +1,10 @@ +package ru.codebattles.backend.entity + +import jakarta.persistence.Entity + +@Entity +data class Checker( + val displayName: String, + val languageHighlightName: String, + val address: String, +) : BaseEntity() diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt new file mode 100644 index 0000000..10f8864 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt @@ -0,0 +1,32 @@ +package ru.codebattles.backend.entity + +import jakarta.persistence.* +import lombok.Builder +import java.util.* + +@Entity +@Table(name = "competitions") +@Builder +data class Competition( + @ManyToMany + val members: MutableSet = mutableSetOf(), + + @ManyToMany + val checkers: MutableSet = mutableSetOf(), + + @ManyToOne + val organizer: User, + + @Column(nullable = false) + val name: String, + + @Column(nullable = false, length = 1000) + val description: String, + + @Column(name = "started_at") + val startedAt: Date? = null, + + @Column(name = "ended_at") + val endedAt: Date? = null, + + ) : BaseEntity() diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt new file mode 100644 index 0000000..595bb42 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt @@ -0,0 +1,14 @@ +package ru.codebattles.backend.entity + +import jakarta.persistence.Entity +import lombok.Data + +@Entity +data class Problem( + val name: String, + val description: String, + val inData: String, + val outData: String, + val tests: String, + val examples: String, +) : BaseEntity() \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt new file mode 100644 index 0000000..76e194c --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt @@ -0,0 +1,26 @@ +package ru.codebattles.backend.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Table +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.core.userdetails.UserDetails + +@Entity +@Table(name = "users_") +data class User( + @Column(name = "username") + var musername: String?, + @Column(name = "password") + var mpassword: String?, +) : UserDetails, BaseEntity() { + override fun getAuthorities(): MutableCollection { + return mutableListOf(SimpleGrantedAuthority("ROLE_USER")) + } + + override fun getPassword() = mpassword + override fun getUsername() = musername + + +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt new file mode 100644 index 0000000..82bb25a --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt @@ -0,0 +1,6 @@ +package ru.codebattles.backend.repository + +import org.springframework.data.jpa.repository.JpaRepository +import ru.codebattles.backend.entity.Competition + +interface CompetitionRepository : JpaRepository \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt new file mode 100644 index 0000000..8a9b806 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt @@ -0,0 +1,11 @@ +package ru.codebattles.backend.repository + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository +import ru.codebattles.backend.entity.User + + +@Repository +interface UserRepository : JpaRepository { + fun findByMusername(username: String): User +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserDetailService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserDetailService.kt new file mode 100644 index 0000000..d6d990c --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserDetailService.kt @@ -0,0 +1,15 @@ +package ru.codebattles.backend.services + +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.stereotype.Component +import ru.codebattles.backend.repository.UserRepository + +@Component +class UserDetailService( + private val userRepository: UserRepository, +) : UserDetailsService { + override fun loadUserByUsername(username: String?): UserDetails { + return userRepository.findByMusername(username!!) + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/HelloWorldController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/HelloWorldController.kt index eec26ef..603369b 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/HelloWorldController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/HelloWorldController.kt @@ -1,12 +1,49 @@ package ru.codebattles.backend.web.controllers +import lombok.AllArgsConstructor +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.transaction.annotation.Transactional import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RestController +import ru.codebattles.backend.entity.Competition +import ru.codebattles.backend.entity.User +import ru.codebattles.backend.repository.CompetitionRepository +import ru.codebattles.backend.repository.UserRepository @RestController class HelloWorldController { + + @Autowired + private lateinit var userRepository: UserRepository + + @Autowired + private lateinit var competitionRepository: CompetitionRepository + + @Autowired + private lateinit var passwordEncoder: PasswordEncoder + @GetMapping fun helloWorld(): String { return "Hello, world!" } + + @Transactional + @PostMapping("/test") + fun testCreateComp( + + ): String { + +// User.with + + val user = User(mpassword = passwordEncoder.encode("admin"), musername = "alex") + + val competition = Competition(name = "123", organizer = user, description = "some description") + + userRepository.save(user) + competitionRepository.save(competition) + + return "ok" + } } \ No newline at end of file From 2640473d729f9ecc906153d99816632291d4fe02 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Thu, 26 Dec 2024 21:55:46 +0300 Subject: [PATCH 006/117] Backend_2: entities and first test endpoint --- BACKEND_V2/build.gradle.kts | 20 ++++++++++++++++ .../ru/codebattles/backend/dto/CheckerDto.kt | 7 ++++++ .../codebattles/backend/dto/CompetitionDto.kt | 13 ++++++++++ .../ru/codebattles/backend/dto/UserDto.kt | 6 +++++ .../backend/dto/mapper/CompetitionsMapper.kt | 9 +++++++ .../backend/dto/mapper/core/AbstractMapper.kt | 7 ++++++ .../backend/entity/CompetitionsProblems.kt | 13 ++++++++++ .../backend/services/CompetitionService.kt | 24 +++++++++++++++++++ .../web/controllers/CompetitionsController.kt | 22 +++++++++++++++++ .../libs/SwaggerRedirectController.kt | 10 ++++++++ 10 files changed, 131 insertions(+) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CheckerDto.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionDto.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserDto.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsMapper.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/CompetitionsProblems.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/libs/SwaggerRedirectController.kt diff --git a/BACKEND_V2/build.gradle.kts b/BACKEND_V2/build.gradle.kts index d86ef63..0c63f21 100644 --- a/BACKEND_V2/build.gradle.kts +++ b/BACKEND_V2/build.gradle.kts @@ -4,6 +4,7 @@ plugins { id("org.springframework.boot") version "3.4.1" id("io.spring.dependency-management") version "1.1.7" kotlin("plugin.jpa") version "1.9.25" + id("org.jetbrains.kotlin.kapt") version "1.9.0" } group = "ru.codebattles" @@ -25,12 +26,19 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-security") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") + implementation("org.springdoc:springdoc-openapi-starter-common:2.1.0") + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testRuntimeOnly("org.junit.platform:junit-platform-launcher") compileOnly("org.projectlombok:lombok") runtimeOnly("org.postgresql:postgresql") annotationProcessor("org.projectlombok:lombok") + + implementation("org.springframework.boot:spring-boot-starter-actuator") + + implementation("org.mapstruct:mapstruct:1.5.5.Final") + kapt("org.mapstruct:mapstruct-processor:1.5.5.Final") } kotlin { @@ -48,3 +56,15 @@ allOpen { tasks.withType { useJUnitPlatform() } + +kapt { + correctErrorTypes = true +} + +sourceSets { + main { + java { + srcDir("build/generated/sources/annotationProcessor/java/main") + } + } +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CheckerDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CheckerDto.kt new file mode 100644 index 0000000..e498e73 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CheckerDto.kt @@ -0,0 +1,7 @@ +package ru.codebattles.backend.dto + +data class CheckerDto( + val id: Long, + val displayName: String, + val languageHighlightName: String, +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionDto.kt new file mode 100644 index 0000000..c341b9a --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionDto.kt @@ -0,0 +1,13 @@ +package ru.codebattles.backend.dto + +import java.time.LocalDateTime + +data class CompetitionDto( + val id: Long, + val members: Set = emptySet(), + val checkers: Set = emptySet(), + val name: String, + val description: String, + val startedAt: LocalDateTime? = null, + val endedAt: LocalDateTime? = null +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserDto.kt new file mode 100644 index 0000000..6dab844 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserDto.kt @@ -0,0 +1,6 @@ +package ru.codebattles.backend.dto + +data class UserDto( + val id: Long, + val username: String, +) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsMapper.kt new file mode 100644 index 0000000..1b7b7c8 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsMapper.kt @@ -0,0 +1,9 @@ +package ru.codebattles.backend.dto.mapper + +import org.mapstruct.Mapper +import ru.codebattles.backend.entity.Competition +import ru.codebattles.backend.dto.CompetitionDto +import ru.codebattles.backend.dto.mapper.core.AbstractMapper + +@Mapper(componentModel = "spring") +interface CompetitionsMapper : AbstractMapper diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt new file mode 100644 index 0000000..02a0c97 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt @@ -0,0 +1,7 @@ +package ru.codebattles.backend.dto.mapper.core + + +interface AbstractMapper { + fun toDto(obj: OBJ): DTO + fun fromDto(obj: DTO): OBJ +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/CompetitionsProblems.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/CompetitionsProblems.kt new file mode 100644 index 0000000..20cb208 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/CompetitionsProblems.kt @@ -0,0 +1,13 @@ +package ru.codebattles.backend.entity + +import jakarta.persistence.Entity +import jakarta.persistence.ManyToOne + +@Entity +data class CompetitionsProblems( + val priority: Int, + @ManyToOne + val competition: Competition, + @ManyToOne + val problem: Problem, +) : BaseEntity() \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt new file mode 100644 index 0000000..a734929 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt @@ -0,0 +1,24 @@ +package ru.codebattles.backend.services + +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Service +import org.springframework.web.server.ResponseStatusException +import ru.codebattles.backend.dto.CompetitionDto +import ru.codebattles.backend.dto.mapper.CompetitionsMapper +import ru.codebattles.backend.repository.CompetitionRepository + +@Service +class CompetitionService( + private val competitionRepository: CompetitionRepository, + private val competitionsMapper: CompetitionsMapper, +) { + fun getById(id: Long): CompetitionDto { + val optionalCompetition = competitionRepository.findById(id) + if (optionalCompetition.isPresent) { + return competitionsMapper.toDto(optionalCompetition.get()) + } + + + throw ResponseStatusException(HttpStatus.NOT_FOUND) + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt new file mode 100644 index 0000000..29f0224 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt @@ -0,0 +1,22 @@ +package ru.codebattles.backend.web.controllers + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import ru.codebattles.backend.dto.CompetitionDto +import ru.codebattles.backend.services.CompetitionService + + +@RestController +@RequestMapping("/api/competitions") +class CompetitionsController { + @Autowired + private lateinit var competitionService: CompetitionService + + @GetMapping("{id}") + fun getById(@PathVariable id: Long): CompetitionDto { + return competitionService.getById(id) + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/libs/SwaggerRedirectController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/libs/SwaggerRedirectController.kt new file mode 100644 index 0000000..86b7479 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/libs/SwaggerRedirectController.kt @@ -0,0 +1,10 @@ +package ru.codebattles.backend.web.controllers.libs + +import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.GetMapping + +@Controller +class SwaggerRedirectController { + @GetMapping("/swagger-ui") + fun redirectToNewUrl() = "redirect:/swagger-ui/index.html" +} From 4624e80c2245743e85374a922c233e9e8dbbf56d Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 27 Dec 2024 13:23:01 +0300 Subject: [PATCH 007/117] Backend_2: add more endpoints --- BACKEND_V2/settings.gradle.kts | 2 + .../backend/config/SecurityConfig.kt | 12 +++++- .../backend/dto/CompetitionCreateDto.kt | 10 +++++ .../codebattles/backend/dto/CompetitionDto.kt | 3 +- .../ru/codebattles/backend/dto/ProblemDto.kt | 12 ++++++ .../dto/mapper/CompetitionsCreateMapper.kt | 19 +++++++++ .../backend/dto/mapper/ProblemsMapper.kt | 9 +++++ .../backend/dto/mapper/core/AbstractMapper.kt | 2 + .../codebattles/backend/entity/Competition.kt | 6 +-- .../ru/codebattles/backend/entity/Problem.kt | 2 +- .../CompetitionProblemsRepository.kt | 8 ++++ .../repository/CompetitionRepository.kt | 5 ++- .../backend/repository/ProblemsRepository.kt | 6 +++ .../backend/services/CompetitionService.kt | 32 +++++++++++++++ .../backend/services/ProblemsService.kt | 40 +++++++++++++++++++ .../CheckerSystemEndpointController.kt | 14 +++++++ .../web/controllers/CompetitionsController.kt | 24 +++++++++-- .../web/controllers/ProblemsController.kt | 31 ++++++++++++++ .../web/entity/checker/CheckerCallback.kt | 4 ++ .../web/entity/checker/ProcessEndStatus.kt | 15 +++++++ .../web/entity/checker/ProgramResult.kt | 10 +++++ .../src/main/resources/application.properties | 3 +- 22 files changed, 255 insertions(+), 14 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionCreateDto.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/ProblemDto.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsCreateMapper.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/ProblemsMapper.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/ProblemsRepository.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CheckerSystemEndpointController.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProblemsController.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/CheckerCallback.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/ProcessEndStatus.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/ProgramResult.kt diff --git a/BACKEND_V2/settings.gradle.kts b/BACKEND_V2/settings.gradle.kts index 2401f0a..4189a93 100644 --- a/BACKEND_V2/settings.gradle.kts +++ b/BACKEND_V2/settings.gradle.kts @@ -1 +1,3 @@ rootProject.name = "backend" +gradle.startParameter.warningMode = WarningMode.None + diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt index af4b3db..1f87393 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt @@ -17,7 +17,17 @@ class SecurityConfig { .csrf { it.disable() } // Отключить CSRF при необходимости .authorizeHttpRequests { it - .requestMatchers("/test/**").permitAll() // Разрешить доступ без авторизации + .requestMatchers("/test/**").permitAll() +// .requestMatchers( +// "/api/auth/login", +// "/api/auth/register", +// "/api/**", +// "/swagger-ui/**", +// "/v3/api-docs/**", +// "/swagger-ui.html", +// "/favicon.ico", +// "/webjars/**" +// ).permitAll()// Разрешить доступ без авторизации .anyRequest().authenticated() // Все остальные запросы требуют авторизации } .formLogin { it } // Включить стандартный логин через форму diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionCreateDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionCreateDto.kt new file mode 100644 index 0000000..952adb1 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionCreateDto.kt @@ -0,0 +1,10 @@ +package ru.codebattles.backend.dto + +import java.time.LocalDateTime + +data class CompetitionCreateDto( + val name: String, + val description: String, + val startedAt: LocalDateTime? = null, + val endedAt: LocalDateTime? = null +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionDto.kt index c341b9a..e083a24 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionDto.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionDto.kt @@ -4,8 +4,7 @@ import java.time.LocalDateTime data class CompetitionDto( val id: Long, - val members: Set = emptySet(), - val checkers: Set = emptySet(), + val checkers: Set? = emptySet(), val name: String, val description: String, val startedAt: LocalDateTime? = null, diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/ProblemDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/ProblemDto.kt new file mode 100644 index 0000000..f6e9a96 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/ProblemDto.kt @@ -0,0 +1,12 @@ +package ru.codebattles.backend.dto + + +data class ProblemDto( + val id: Long, + val name: String, + val description: String, + val inData: String, + val outData: String, + val examples: String, + val public: Boolean? = false +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsCreateMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsCreateMapper.kt new file mode 100644 index 0000000..225e619 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsCreateMapper.kt @@ -0,0 +1,19 @@ +package ru.codebattles.backend.dto.mapper + +import org.mapstruct.Mapper +import org.mapstruct.Mapping +import org.mapstruct.NullValuePropertyMappingStrategy +import ru.codebattles.backend.dto.CompetitionCreateDto +import ru.codebattles.backend.dto.mapper.core.AbstractMapper +import ru.codebattles.backend.entity.Competition +import ru.codebattles.backend.entity.User + +@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) +interface CompetitionsCreateMapper : AbstractMapper { + @Deprecated("Use method with other signature") + override fun fromDto(obj: CompetitionCreateDto): Competition + + @Mapping(target = "organizer", source = "user") + fun fromDto(dto: CompetitionCreateDto, user: User): Competition + +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/ProblemsMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/ProblemsMapper.kt new file mode 100644 index 0000000..bcebcc4 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/ProblemsMapper.kt @@ -0,0 +1,9 @@ +package ru.codebattles.backend.dto.mapper + +import org.mapstruct.Mapper +import ru.codebattles.backend.dto.ProblemDto +import ru.codebattles.backend.dto.mapper.core.AbstractMapper +import ru.codebattles.backend.entity.Problem + +@Mapper(componentModel = "spring") +interface ProblemsMapper : AbstractMapper diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt index 02a0c97..d4861e9 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt @@ -4,4 +4,6 @@ package ru.codebattles.backend.dto.mapper.core interface AbstractMapper { fun toDto(obj: OBJ): DTO fun fromDto(obj: DTO): OBJ + fun fromDtoS(obj: List): List + fun toDtoS(obj: List): List } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt index 10f8864..0b39bc9 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt @@ -1,18 +1,16 @@ package ru.codebattles.backend.entity import jakarta.persistence.* -import lombok.Builder import java.util.* @Entity @Table(name = "competitions") -@Builder data class Competition( @ManyToMany - val members: MutableSet = mutableSetOf(), + val members: MutableSet? = mutableSetOf(), @ManyToMany - val checkers: MutableSet = mutableSetOf(), + val checkers: MutableSet? = mutableSetOf(), @ManyToOne val organizer: User, diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt index 595bb42..c62ec5f 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt @@ -1,7 +1,6 @@ package ru.codebattles.backend.entity import jakarta.persistence.Entity -import lombok.Data @Entity data class Problem( @@ -11,4 +10,5 @@ data class Problem( val outData: String, val tests: String, val examples: String, + val public: Boolean? = false ) : BaseEntity() \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt new file mode 100644 index 0000000..9996317 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt @@ -0,0 +1,8 @@ +package ru.codebattles.backend.repository + +import org.springframework.data.jpa.repository.JpaRepository +import ru.codebattles.backend.entity.CompetitionsProblems + +interface CompetitionProblemsRepository : JpaRepository { + fun getAllByCompetitionId(id: Long): Iterable +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt index 82bb25a..4187903 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt @@ -2,5 +2,8 @@ package ru.codebattles.backend.repository import org.springframework.data.jpa.repository.JpaRepository import ru.codebattles.backend.entity.Competition +import ru.codebattles.backend.entity.User -interface CompetitionRepository : JpaRepository \ No newline at end of file +interface CompetitionRepository : JpaRepository { + fun getByMembersContaining(user: User): List +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/ProblemsRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/ProblemsRepository.kt new file mode 100644 index 0000000..76b15aa --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/ProblemsRepository.kt @@ -0,0 +1,6 @@ +package ru.codebattles.backend.repository + +import org.springframework.data.jpa.repository.JpaRepository +import ru.codebattles.backend.entity.Problem + +interface ProblemsRepository : JpaRepository {} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt index a734929..7ecb939 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt @@ -3,14 +3,24 @@ package ru.codebattles.backend.services import org.springframework.http.HttpStatus import org.springframework.stereotype.Service import org.springframework.web.server.ResponseStatusException +import ru.codebattles.backend.dto.CompetitionCreateDto import ru.codebattles.backend.dto.CompetitionDto +import ru.codebattles.backend.dto.ProblemDto +import ru.codebattles.backend.dto.mapper.CompetitionsCreateMapper import ru.codebattles.backend.dto.mapper.CompetitionsMapper +import ru.codebattles.backend.dto.mapper.ProblemsMapper +import ru.codebattles.backend.entity.CompetitionsProblems +import ru.codebattles.backend.entity.User +import ru.codebattles.backend.repository.CompetitionProblemsRepository import ru.codebattles.backend.repository.CompetitionRepository @Service class CompetitionService( private val competitionRepository: CompetitionRepository, + private val competitionProblemsRepository: CompetitionProblemsRepository, private val competitionsMapper: CompetitionsMapper, + private val problemsMapper: ProblemsMapper, + private val competitionsCreateMapper: CompetitionsCreateMapper ) { fun getById(id: Long): CompetitionDto { val optionalCompetition = competitionRepository.findById(id) @@ -21,4 +31,26 @@ class CompetitionService( throw ResponseStatusException(HttpStatus.NOT_FOUND) } + + fun getProblemsById(id: Long): List { + val extractProblemFromDataclass: (trans: CompetitionsProblems) -> ProblemDto = + { obj -> problemsMapper.toDto(obj.problem)} + + return competitionProblemsRepository.getAllByCompetitionId(id).map(extractProblemFromDataclass) + } + + fun getAllByUser(user: User): List { + return competitionsMapper.toDtoS( + competitionRepository.getByMembersContaining(user) + ) + } + + fun create(competitionDto: CompetitionCreateDto, user: User): CompetitionDto { + val convertedFromDto = competitionsCreateMapper.fromDto(competitionDto, user) + val competition = competitionRepository.save(convertedFromDto) + + println() + + return competitionsMapper.toDto(competition) + } } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt new file mode 100644 index 0000000..29bc32e --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt @@ -0,0 +1,40 @@ +package ru.codebattles.backend.services + +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Service +import org.springframework.web.server.ResponseStatusException +import ru.codebattles.backend.dto.ProblemDto +import ru.codebattles.backend.dto.mapper.ProblemsMapper +import ru.codebattles.backend.repository.ProblemsRepository + +@Service +class ProblemsService( + val problemsRepository: ProblemsRepository, + val problemsMapper: ProblemsMapper, +) { + fun getById(id: Long): ProblemDto { + val optionalProblem = problemsRepository.findById(id) + if (optionalProblem.isPresent) { + return problemsMapper.toDto(optionalProblem.get()) + } + + + throw ResponseStatusException(HttpStatus.NOT_FOUND) + } + + + fun create(problemDto: ProblemDto): ProblemDto { + val convertedFromDto = problemsMapper.fromDto(problemDto) + val competition = problemsRepository.save(convertedFromDto) + + println() + + return problemsMapper.toDto(competition) + } + + fun getAll(): Iterable { + return problemsMapper.toDtoS( + problemsRepository.findAll() + ) + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CheckerSystemEndpointController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CheckerSystemEndpointController.kt new file mode 100644 index 0000000..53cbe60 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CheckerSystemEndpointController.kt @@ -0,0 +1,14 @@ +package ru.codebattles.backend.web.controllers + +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController +import ru.codebattles.backend.web.entity.checker.CheckerCallback + +@RestController +class CheckerSystemEndpointController { + @PostMapping("/api/check_system_callback") + fun checkerCallBack(@RequestBody data: CheckerCallback) { + TODO("Not implemented yet") + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt index 29f0224..b77733d 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt @@ -1,11 +1,12 @@ package ru.codebattles.backend.web.controllers import org.springframework.beans.factory.annotation.Autowired -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.* +import ru.codebattles.backend.dto.CompetitionCreateDto import ru.codebattles.backend.dto.CompetitionDto +import ru.codebattles.backend.dto.ProblemDto +import ru.codebattles.backend.entity.User import ru.codebattles.backend.services.CompetitionService @@ -15,8 +16,23 @@ class CompetitionsController { @Autowired private lateinit var competitionService: CompetitionService + @PostMapping + fun create(@RequestBody instance: CompetitionCreateDto, @AuthenticationPrincipal user: User): CompetitionDto { + return competitionService.create(instance, user) + } + @GetMapping("{id}") fun getById(@PathVariable id: Long): CompetitionDto { return competitionService.getById(id) } + + @GetMapping("{id}/problems") + fun getProblemsById(@PathVariable id: Long): List { + return competitionService.getProblemsById(id) + } + + @GetMapping + fun getAllAvaliableForUser(@AuthenticationPrincipal user: User): List { + return competitionService.getAllByUser(user) + } } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProblemsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProblemsController.kt new file mode 100644 index 0000000..d2cc5da --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProblemsController.kt @@ -0,0 +1,31 @@ +package ru.codebattles.backend.web.controllers + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.* +import ru.codebattles.backend.dto.ProblemDto +import ru.codebattles.backend.entity.User +import ru.codebattles.backend.services.ProblemsService + + +@RestController +@RequestMapping("/api/problems") +class ProblemsController { + @Autowired + private lateinit var problemsService: ProblemsService + + @PostMapping + fun create(@RequestBody instance: ProblemDto, @AuthenticationPrincipal user: User): ProblemDto { + return problemsService.create(instance) + } + + @GetMapping("{id}") + fun getById(@PathVariable id: Long): ProblemDto { + return problemsService.getById(id) + } + + @GetMapping + fun getAll(): Iterable { + return problemsService.getAll() + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/CheckerCallback.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/CheckerCallback.kt new file mode 100644 index 0000000..da4e28b --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/CheckerCallback.kt @@ -0,0 +1,4 @@ +package ru.codebattles.backend.web.entity.checker + +@JvmRecord +data class CheckerCallback(val results: List, val meta: String) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/ProcessEndStatus.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/ProcessEndStatus.kt new file mode 100644 index 0000000..94bed97 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/ProcessEndStatus.kt @@ -0,0 +1,15 @@ +package ru.codebattles.backend.web.entity.checker + +enum class ProcessEndStatus(private val msg: String) { + SUCCESS("OK"), + RUNTIME_ERROR("RE"), + COMPILE_ERROR("CE"), + TIME_LIMIT("TL"), + WRONG_ANSWER("WA"), + NOT_EXECUTED("NE"), + ; + + override fun toString(): String { + return msg + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/ProgramResult.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/ProgramResult.kt new file mode 100644 index 0000000..7ae02dc --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/checker/ProgramResult.kt @@ -0,0 +1,10 @@ +package ru.codebattles.backend.web.entity.checker + + +@JvmRecord +data class ProgramResult( + val success: Boolean, + val out: String, + val msg: ProcessEndStatus, + val time: Int +) diff --git a/BACKEND_V2/src/main/resources/application.properties b/BACKEND_V2/src/main/resources/application.properties index d8b79c4..333a6fb 100644 --- a/BACKEND_V2/src/main/resources/application.properties +++ b/BACKEND_V2/src/main/resources/application.properties @@ -3,4 +3,5 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/codebattles spring.datasource.username=postgres spring.datasource.password=admin spring.jpa.hibernate.ddl-auto=update -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +spring.security.filter.order=10 \ No newline at end of file From 758024bd67863a8ec4541ef0c10c6f5fe58ded6f Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 27 Dec 2024 21:39:26 +0300 Subject: [PATCH 008/117] Backend_2: add auth --- BACKEND_V2/build.gradle.kts | 4 ++ .../backend/config/SecurityConfig.kt | 41 ------------ .../backend/core/config/SecurityConfig.kt | 66 +++++++++++++++++++ .../core/filter/JwtAuthenticationFilter.kt | 38 +++++++++++ .../backend/services/JwtService.kt | 45 +++++++++++++ .../backend/services/UserService.kt | 15 +++++ .../web/controllers/AuthContoroller.kt | 30 +++++++++ .../backend/web/entity/auth/AuthRequest.kt | 3 + .../backend/web/entity/auth/AuthResponse.kt | 3 + .../src/main/resources/application.properties | 1 - 10 files changed, 204 insertions(+), 42 deletions(-) delete mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/filter/JwtAuthenticationFilter.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserService.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/AuthContoroller.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/auth/AuthRequest.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/auth/AuthResponse.kt diff --git a/BACKEND_V2/build.gradle.kts b/BACKEND_V2/build.gradle.kts index 0c63f21..0f20e83 100644 --- a/BACKEND_V2/build.gradle.kts +++ b/BACKEND_V2/build.gradle.kts @@ -39,6 +39,10 @@ dependencies { implementation("org.mapstruct:mapstruct:1.5.5.Final") kapt("org.mapstruct:mapstruct-processor:1.5.5.Final") + + implementation("io.jsonwebtoken:jjwt-api:0.11.5") + implementation("io.jsonwebtoken:jjwt-impl:0.11.5") + implementation("io.jsonwebtoken:jjwt-jackson:0.11.5") } kotlin { diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt deleted file mode 100644 index 1f87393..0000000 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/config/SecurityConfig.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ru.codebattles.backend.config - -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder -import org.springframework.security.crypto.password.PasswordEncoder -import org.springframework.security.web.SecurityFilterChain - - -@Configuration -class SecurityConfig { - - @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http - .csrf { it.disable() } // Отключить CSRF при необходимости - .authorizeHttpRequests { - it - .requestMatchers("/test/**").permitAll() -// .requestMatchers( -// "/api/auth/login", -// "/api/auth/register", -// "/api/**", -// "/swagger-ui/**", -// "/v3/api-docs/**", -// "/swagger-ui.html", -// "/favicon.ico", -// "/webjars/**" -// ).permitAll()// Разрешить доступ без авторизации - .anyRequest().authenticated() // Все остальные запросы требуют авторизации - } - .formLogin { it } // Включить стандартный логин через форму - return http.build() - } - - @Bean - fun encoder(): PasswordEncoder { - return BCryptPasswordEncoder() - } -} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt new file mode 100644 index 0000000..9cb91e2 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt @@ -0,0 +1,66 @@ +package ru.codebattles.backend.core.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.web.SecurityFilterChain +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter +import ru.codebattles.backend.core.filter.JwtAuthenticationFilter + + +@Configuration +class SecurityConfig( + private val jwtAuthenticationFilter: JwtAuthenticationFilter +) { + + @Bean + fun authenticationManager(authConfig: AuthenticationConfiguration): AuthenticationManager { + return authConfig.authenticationManager + } +// +// @Bean +// fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { +// http +// .csrf { it.disable() } // Отключить CSRF при необходимости +// .authorizeHttpRequests { +// it +// .requestMatchers("/test/**").permitAll() +// .requestMatchers("/api/auth/login").permitAll() +//// .requestMatchers( +//// "/api/auth/login", +//// "/api/auth/register", +//// "/api/**", +//// "/swagger-ui/**", +//// "/v3/api-docs/**", +//// "/swagger-ui.html", +//// "/favicon.ico", +//// "/webjars/**" +//// ).permitAll()// Разрешить доступ без авторизации +// .anyRequest().authenticated() // Все остальные запросы требуют авторизации +// .and() +// .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) +// } +// .formLogin { it } // Включить стандартный логин через форму +// return http.build() +// } + + @Bean + fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + http.csrf().disable() + .authorizeRequests() + .requestMatchers("/api/auth/**").permitAll() + .anyRequest().authenticated() + .and() + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) + return http.build() + } + + @Bean + fun encoder(): PasswordEncoder { + return BCryptPasswordEncoder() + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/filter/JwtAuthenticationFilter.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/filter/JwtAuthenticationFilter.kt new file mode 100644 index 0000000..a46e013 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/filter/JwtAuthenticationFilter.kt @@ -0,0 +1,38 @@ +package ru.codebattles.backend.core.filter + +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter +import ru.codebattles.backend.services.JwtService +import ru.codebattles.backend.services.UserService + +@Component +class JwtAuthenticationFilter( + private val jwtService: JwtService, + val userService: UserService +) : OncePerRequestFilter() { + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + val authHeader = request.getHeader("Authorization") + if (authHeader != null && authHeader.startsWith("Bearer ")) { + val token = authHeader.substring(7) + if (jwtService.validateToken(token)) { + val username = jwtService.getUsernameFromToken(token) + val user = userService.getByUsername(username) + val authentication = UsernamePasswordAuthenticationToken(user, null, user.authorities) + authentication.details = WebAuthenticationDetailsSource().buildDetails(request) + SecurityContextHolder.getContext().authentication = authentication + } + } + filterChain.doFilter(request, response) + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt new file mode 100644 index 0000000..3bd6bde --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt @@ -0,0 +1,45 @@ +package ru.codebattles.backend.services + + +import io.jsonwebtoken.Jwts +import io.jsonwebtoken.SignatureAlgorithm +import io.jsonwebtoken.security.Keys +import org.springframework.stereotype.Service +import java.security.Key +import java.util.* + +@Service +class JwtService { + + private val jwtSecret: Key = Keys.secretKeyFor(SignatureAlgorithm.HS256) + private val jwtExpirationMs = 3600000 // 1 hour + + fun generateToken(username: String): String { + return Jwts.builder() + .setSubject(username) + .setIssuedAt(Date()) + .setExpiration(Date(System.currentTimeMillis() + jwtExpirationMs)) + .signWith(jwtSecret) + .compact() + } + + fun validateToken(token: String): Boolean { + try { + val claims = Jwts.parserBuilder() + .setSigningKey(jwtSecret) + .build() + .parseClaimsJws(token) + return !claims.body.expiration.before(Date()) + } catch (e: Exception) { + return false + } + } + + fun getUsernameFromToken(token: String): String { + val claims = Jwts.parserBuilder() + .setSigningKey(jwtSecret) + .build() + .parseClaimsJws(token) + return claims.body.subject + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserService.kt new file mode 100644 index 0000000..aaae56b --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserService.kt @@ -0,0 +1,15 @@ +package ru.codebattles.backend.services + + +import org.springframework.stereotype.Service +import ru.codebattles.backend.entity.User +import ru.codebattles.backend.repository.UserRepository + +@Service +class UserService( + private val userRepository: UserRepository +) { + fun getByUsername(username: String): User { + return userRepository.findByMusername(username) + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/AuthContoroller.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/AuthContoroller.kt new file mode 100644 index 0000000..a2676bc --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/AuthContoroller.kt @@ -0,0 +1,30 @@ +package ru.codebattles.backend.web.controllers + +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import ru.codebattles.backend.services.JwtService +import ru.codebattles.backend.web.entity.auth.AuthRequest +import ru.codebattles.backend.web.entity.auth.AuthResponse + +@RestController +@RequestMapping("/api/auth") +class AuthController( + private val authenticationManager: AuthenticationManager, + private val jwtService: JwtService +) { + + @PostMapping("/login") + fun login(@RequestBody authRequest: AuthRequest): AuthResponse { + val authentication = authenticationManager.authenticate( + UsernamePasswordAuthenticationToken(authRequest.username, authRequest.password) + ) + val token = jwtService.generateToken(authentication.name) + return AuthResponse(token) + } +} + + diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/auth/AuthRequest.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/auth/AuthRequest.kt new file mode 100644 index 0000000..15d752c --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/auth/AuthRequest.kt @@ -0,0 +1,3 @@ +package ru.codebattles.backend.web.entity.auth + +data class AuthRequest(val username: String, val password: String) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/auth/AuthResponse.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/auth/AuthResponse.kt new file mode 100644 index 0000000..adcf8be --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/auth/AuthResponse.kt @@ -0,0 +1,3 @@ +package ru.codebattles.backend.web.entity.auth + +data class AuthResponse(val token: String) \ No newline at end of file diff --git a/BACKEND_V2/src/main/resources/application.properties b/BACKEND_V2/src/main/resources/application.properties index 333a6fb..9366a92 100644 --- a/BACKEND_V2/src/main/resources/application.properties +++ b/BACKEND_V2/src/main/resources/application.properties @@ -4,4 +4,3 @@ spring.datasource.username=postgres spring.datasource.password=admin spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect -spring.security.filter.order=10 \ No newline at end of file From a454939b86b6a03e6d7e7307d21b06681f9ef845 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 28 Dec 2024 21:36:20 +0300 Subject: [PATCH 009/117] Backend_2: add auth and some api --- .../backend/core/config/SecurityConfig.kt | 28 ++++++++++++++ .../backend/dto/CompetitionsProblemsDto.kt | 13 +++++++ .../dto/mapper/CompetitionsProblemsMapper.kt | 11 ++++++ .../ru/codebattles/backend/entity/Answer.kt | 6 +++ .../backend/repository/AnswerRepository.kt | 6 +++ .../backend/repository/CheckerRepository.kt | 6 +++ .../CompetitionProblemsRepository.kt | 1 + .../backend/services/AnswerService.kt | 37 +++++++++++++++++++ .../backend/services/CompetitionService.kt | 15 +++++--- .../web/controllers/CompetitionsController.kt | 19 +++++++++- 10 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionsProblemsDto.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsProblemsMapper.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CheckerRepository.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt index 9cb91e2..ec571f2 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt @@ -9,7 +9,11 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter +import org.springframework.web.cors.CorsConfiguration +import org.springframework.web.cors.CorsConfigurationSource +import org.springframework.web.cors.UrlBasedCorsConfigurationSource import ru.codebattles.backend.core.filter.JwtAuthenticationFilter +import java.util.* @Configuration @@ -51,8 +55,20 @@ class SecurityConfig( @Bean fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { http.csrf().disable() + .cors() + .and() .authorizeRequests() .requestMatchers("/api/auth/**").permitAll() + .requestMatchers( + "/api/auth/login", + "/api/auth/register", + "/api/**", + "/swagger-ui/**", + "/v3/api-docs/**", + "/swagger-ui.html", + "/favicon.ico", + "/webjars/**" + ).permitAll() .anyRequest().authenticated() .and() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) @@ -63,4 +79,16 @@ class SecurityConfig( fun encoder(): PasswordEncoder { return BCryptPasswordEncoder() } + + @Bean + fun corsConfigurationSource(): CorsConfigurationSource { + val configuration = CorsConfiguration() + configuration.allowedOrigins = listOf("http://localhost:5173") + configuration.allowedMethods = listOf("*") + configuration.allowedHeaders = listOf("*") + val source = UrlBasedCorsConfigurationSource() + source.registerCorsConfiguration("/**", configuration) + return source + } + } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionsProblemsDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionsProblemsDto.kt new file mode 100644 index 0000000..e5d51be --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CompetitionsProblemsDto.kt @@ -0,0 +1,13 @@ +package ru.codebattles.backend.dto + +import jakarta.persistence.Entity +import jakarta.persistence.ManyToOne +import ru.codebattles.backend.entity.BaseEntity +import ru.codebattles.backend.entity.Competition +import ru.codebattles.backend.entity.Problem + +data class CompetitionsProblemsDto( + val id: Long, + val priority: Int, + val problem: ProblemDto, +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsProblemsMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsProblemsMapper.kt new file mode 100644 index 0000000..461a749 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsProblemsMapper.kt @@ -0,0 +1,11 @@ +package ru.codebattles.backend.dto.mapper + +import org.mapstruct.Mapper +import ru.codebattles.backend.entity.Competition +import ru.codebattles.backend.dto.CompetitionDto +import ru.codebattles.backend.dto.CompetitionsProblemsDto +import ru.codebattles.backend.dto.mapper.core.AbstractMapper +import ru.codebattles.backend.entity.CompetitionsProblems + +@Mapper(componentModel = "spring") +interface CompetitionsProblemsMapper : AbstractMapper diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Answer.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Answer.kt index eb056f6..2b2fb37 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Answer.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Answer.kt @@ -20,5 +20,11 @@ data class Answer( @Enumerated(EnumType.STRING) val status: AnswerStatus = AnswerStatus.IN_PROGRESS, val score: Int = 0, + + val code: String, + + @ManyToOne + val checker: Checker + ) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt new file mode 100644 index 0000000..6953c29 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt @@ -0,0 +1,6 @@ +package ru.codebattles.backend.repository + +import org.springframework.data.jpa.repository.JpaRepository +import ru.codebattles.backend.entity.Answer + +interface AnswerRepository : JpaRepository \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CheckerRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CheckerRepository.kt new file mode 100644 index 0000000..88672fc --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CheckerRepository.kt @@ -0,0 +1,6 @@ +package ru.codebattles.backend.repository + +import org.springframework.data.jpa.repository.JpaRepository +import ru.codebattles.backend.entity.Checker + +interface CheckerRepository : JpaRepository \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt index 9996317..6ad183a 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt @@ -5,4 +5,5 @@ import ru.codebattles.backend.entity.CompetitionsProblems interface CompetitionProblemsRepository : JpaRepository { fun getAllByCompetitionId(id: Long): Iterable + fun getFirstByCompetitionIdAndProblemId(competition_id: Long, problem_id: Long): CompetitionsProblems } \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt new file mode 100644 index 0000000..214fcce --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt @@ -0,0 +1,37 @@ +package ru.codebattles.backend.services + +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Service +import org.springframework.web.server.ResponseStatusException +import ru.codebattles.backend.dto.CompetitionCreateDto +import ru.codebattles.backend.dto.CompetitionDto +import ru.codebattles.backend.dto.CompetitionsProblemsDto +import ru.codebattles.backend.dto.mapper.CompetitionsCreateMapper +import ru.codebattles.backend.dto.mapper.CompetitionsMapper +import ru.codebattles.backend.dto.mapper.CompetitionsProblemsMapper +import ru.codebattles.backend.dto.mapper.ProblemsMapper +import ru.codebattles.backend.entity.Answer +import ru.codebattles.backend.entity.CompetitionsProblems +import ru.codebattles.backend.entity.User +import ru.codebattles.backend.repository.AnswerRepository +import ru.codebattles.backend.repository.CheckerRepository +import ru.codebattles.backend.repository.CompetitionProblemsRepository +import ru.codebattles.backend.repository.CompetitionRepository + +@Service +class AnswerService( + val answerRepository: AnswerRepository, + val checkerRepository: CheckerRepository, + val competitionRepository: CompetitionRepository +) { + fun createAnswer(user: User) { + answerRepository.save( + Answer( + competition = competitionRepository.getById(1), + checker = checkerRepository.getById(1), + user = user, + code = "print(\"hello, world\")" + ) + ) + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt index 7ecb939..e28be07 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt @@ -5,9 +5,10 @@ import org.springframework.stereotype.Service import org.springframework.web.server.ResponseStatusException import ru.codebattles.backend.dto.CompetitionCreateDto import ru.codebattles.backend.dto.CompetitionDto -import ru.codebattles.backend.dto.ProblemDto +import ru.codebattles.backend.dto.CompetitionsProblemsDto import ru.codebattles.backend.dto.mapper.CompetitionsCreateMapper import ru.codebattles.backend.dto.mapper.CompetitionsMapper +import ru.codebattles.backend.dto.mapper.CompetitionsProblemsMapper import ru.codebattles.backend.dto.mapper.ProblemsMapper import ru.codebattles.backend.entity.CompetitionsProblems import ru.codebattles.backend.entity.User @@ -18,6 +19,7 @@ import ru.codebattles.backend.repository.CompetitionRepository class CompetitionService( private val competitionRepository: CompetitionRepository, private val competitionProblemsRepository: CompetitionProblemsRepository, + private val competitionsProblemsMapper: CompetitionsProblemsMapper, private val competitionsMapper: CompetitionsMapper, private val problemsMapper: ProblemsMapper, private val competitionsCreateMapper: CompetitionsCreateMapper @@ -32,11 +34,14 @@ class CompetitionService( throw ResponseStatusException(HttpStatus.NOT_FOUND) } - fun getProblemsById(id: Long): List { - val extractProblemFromDataclass: (trans: CompetitionsProblems) -> ProblemDto = - { obj -> problemsMapper.toDto(obj.problem)} + fun getProblemsById(id: Long): Iterable { + return competitionProblemsRepository.getAllByCompetitionId(id) + } - return competitionProblemsRepository.getAllByCompetitionId(id).map(extractProblemFromDataclass) + fun getProblemById(id: Long, problemId: Long): CompetitionsProblemsDto { + return competitionsProblemsMapper.toDto( + competitionProblemsRepository.getFirstByCompetitionIdAndProblemId(id, problemId) + ) } fun getAllByUser(user: User): List { diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt index b77733d..60945bb 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt @@ -5,8 +5,11 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.* import ru.codebattles.backend.dto.CompetitionCreateDto import ru.codebattles.backend.dto.CompetitionDto +import ru.codebattles.backend.dto.CompetitionsProblemsDto import ru.codebattles.backend.dto.ProblemDto +import ru.codebattles.backend.entity.CompetitionsProblems import ru.codebattles.backend.entity.User +import ru.codebattles.backend.services.AnswerService import ru.codebattles.backend.services.CompetitionService @@ -15,6 +18,8 @@ import ru.codebattles.backend.services.CompetitionService class CompetitionsController { @Autowired private lateinit var competitionService: CompetitionService + @Autowired + private lateinit var answerService: AnswerService @PostMapping fun create(@RequestBody instance: CompetitionCreateDto, @AuthenticationPrincipal user: User): CompetitionDto { @@ -26,11 +31,23 @@ class CompetitionsController { return competitionService.getById(id) } + @PostMapping("{id}/send") + fun send(@PathVariable id: Long, @AuthenticationPrincipal user: User): String { + answerService.createAnswer(user) + + return "aboba" + } + @GetMapping("{id}/problems") - fun getProblemsById(@PathVariable id: Long): List { + fun getProblemsByCompetition(@PathVariable id: Long): Iterable { return competitionService.getProblemsById(id) } + @GetMapping("{compId}/problems/{id}") + fun getProblemsByIdByCompetition(@PathVariable compId: Long, @PathVariable id: Long): CompetitionsProblemsDto { + return competitionService.getProblemById(compId, id) + } + @GetMapping fun getAllAvaliableForUser(@AuthenticationPrincipal user: User): List { return competitionService.getAllByUser(user) From aead653701e85ac97eb1d1ed2193d0993f51abca Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 28 Dec 2024 21:36:34 +0300 Subject: [PATCH 010/117] frontend_2: new api --- FRONTEND_V2/src/App.jsx | 6 ++- FRONTEND_V2/src/hooks/useGetAPI.js | 14 +++++-- FRONTEND_V2/src/pages/ChampsPage.jsx | 33 ++++++++++++++++ FRONTEND_V2/src/pages/LoginPage.jsx | 26 +++++++----- FRONTEND_V2/src/pages/ProblemsListPage.jsx | 25 ++++++------ FRONTEND_V2/src/pages/SeeProblemPage.jsx | 46 ++++++++++++++-------- FRONTEND_V2/src/utils/consts.js | 1 + FRONTEND_V2/src/utils/settings.js | 4 +- 8 files changed, 109 insertions(+), 46 deletions(-) create mode 100644 FRONTEND_V2/src/pages/ChampsPage.jsx diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index a24770a..589a7b3 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -13,6 +13,7 @@ import SeeSendPage from "./pages/SeeSendPage.jsx"; import StatusesPage from "./pages/StatusesPage.jsx"; import {BrowserRouter, Route, Routes} from "react-router-dom"; import LoginPage from "./pages/LoginPage.jsx"; +import ChampsPage from "./pages/ChampsPage.jsx"; function App() { @@ -25,13 +26,14 @@ function App() { }/> }/> - }/> - }/> + }/> + }/> }/> }/> }/> }/> }/> + }/> diff --git a/FRONTEND_V2/src/hooks/useGetAPI.js b/FRONTEND_V2/src/hooks/useGetAPI.js index 485605e..ae42322 100644 --- a/FRONTEND_V2/src/hooks/useGetAPI.js +++ b/FRONTEND_V2/src/hooks/useGetAPI.js @@ -11,15 +11,21 @@ const LOCALSTORAGE_PREFIX = "__api_cache=" function useCachedGetAPI( url = defaultUrl, - onError = defaultEmptyFunction + onError = defaultEmptyFunction, + defaultState={} ) { - const [data, setData] = useState({}); + const [data, setData] = useState(defaultState); const navigate = useNavigate() const apiGetData = () => { axios .get(url, - {headers: {'Content-Type': 'application/json'}}) + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` + } + }) .then(({data}) => { setData(data) localStorage.setItem(LOCALSTORAGE_PREFIX + url, JSON.stringify(data)) @@ -46,7 +52,7 @@ function useCachedGetAPI( useEffect(() => { getData(); - },[]); + }, []); return [data, updateCallback]; } diff --git a/FRONTEND_V2/src/pages/ChampsPage.jsx b/FRONTEND_V2/src/pages/ChampsPage.jsx new file mode 100644 index 0000000..6121b6f --- /dev/null +++ b/FRONTEND_V2/src/pages/ChampsPage.jsx @@ -0,0 +1,33 @@ +import Card from "../components/bootstrap/Card.jsx"; +import ResponsiveTable from "../components/bootstrap/ResponsiveTable.jsx"; +import useCachedGetAPI from "../hooks/useGetAPI.js"; +import {useEffect} from "react"; +import {Link} from "react-router-dom"; + +const ChampsPage = () => { + + const [data, update] = useCachedGetAPI("/api/competitions",() => {}, []); + + useEffect(() => { + update() + }, []); + + console.log(data) + + return ( + <> + { + data?.map(elem => { + return +
Идет
+

{elem.name}

+

{elem.description}

+ Войти +
+ }) + } + + ); +}; + +export default ChampsPage; diff --git a/FRONTEND_V2/src/pages/LoginPage.jsx b/FRONTEND_V2/src/pages/LoginPage.jsx index 0bc106a..9e3bd2a 100644 --- a/FRONTEND_V2/src/pages/LoginPage.jsx +++ b/FRONTEND_V2/src/pages/LoginPage.jsx @@ -1,4 +1,4 @@ -import {useState} from 'react'; +import {useEffect, useState} from 'react'; import Card from "../components/bootstrap/Card.jsx"; import {useNavigate} from "react-router-dom"; import {useForm} from "react-hook-form"; @@ -13,13 +13,19 @@ const LoginPage = () => { const [hasErrorInData, setHasErrorInData] = useState(false); const onSubmit = (data) => { - console.log(data) - axios.post('/api/login', data) + // console.log(data) + axios.post('/api/auth/login', data) + .then((req) => localStorage.setItem(constants.LOCALSTORAGE_JWT, req.data.token)) + .then((dta) => console.log(dta)) .then(() => localStorage.setItem(constants.LOCALSTORAGE_AUTH_KEY, "true")) - .then(() => navigate("/problems")) + .then(() => navigate("/champs")) .catch(() => setHasErrorInData(true)) } + useEffect(() => { + if (localStorage.getItem(constants.LOCALSTORAGE_AUTH_KEY) === "true") navigate("/champs") + }, []); + return (
@@ -28,14 +34,14 @@ const LoginPage = () => { { hasErrorInData && (

Неправильные данные

) } -
- - -
+ {/*
*/} + {/* */} + {/* */} + {/*
*/}
-
diff --git a/FRONTEND_V2/src/pages/ProblemsListPage.jsx b/FRONTEND_V2/src/pages/ProblemsListPage.jsx index 3f262dc..95a29b8 100644 --- a/FRONTEND_V2/src/pages/ProblemsListPage.jsx +++ b/FRONTEND_V2/src/pages/ProblemsListPage.jsx @@ -1,13 +1,15 @@ import {useEffect} from 'react'; import Card from "../components/bootstrap/Card.jsx"; -import {Link} from "react-router-dom"; +import {Link, useParams} from "react-router-dom"; import useCachedGetAPI from "../hooks/useGetAPI.js"; import UserLoginRequired from "../components/UserLoginRequired.jsx"; import ResponsiveTable from "../components/bootstrap/ResponsiveTable.jsx"; const ProblemsListPage = () => { - const [data, update] = useCachedGetAPI("/api/problems"); + let { id } = useParams(); + + const [data, update] = useCachedGetAPI(`/api/competitions/${id}/problems`, () => {}, []); useEffect(() => { update(); @@ -30,19 +32,18 @@ const ProblemsListPage = () => { { - data.problems && - Object.keys(data.problems).map(letter => { - const is_quiz = data?.is_quizes?.[letter] - const link = is_quiz ? (`/problems/${letter}/quizz`) : (`/problems/${letter}`) - const link_icon_css_class = is_quiz ? (`bi-card-checklist`) : (`bi-braces`) + data.map(data => { + + const link_icon_css_class = (`bi-braces`) - return ( + return ( + - {letter} + A {data.id} - - - {data.problems[letter]} + + + {data.problem?.name} ) diff --git a/FRONTEND_V2/src/pages/SeeProblemPage.jsx b/FRONTEND_V2/src/pages/SeeProblemPage.jsx index 53792f5..86e46d7 100644 --- a/FRONTEND_V2/src/pages/SeeProblemPage.jsx +++ b/FRONTEND_V2/src/pages/SeeProblemPage.jsx @@ -8,18 +8,21 @@ import axios from "axios"; import LazyCodeEditor from "../components/lazy/LazyCodeEditor.jsx"; import UserLoginRequired from "../components/UserLoginRequired.jsx"; import Markdown from "../components/wraps/Markdown.jsx"; +import constants from "../utils/consts.js"; const SeeProblemPage = () => { - const {letter} = useParams(); + const {compId, id} = useParams(); const navigate = useNavigate(); - const [data, update] = useCachedGetAPI(`/api/problem/${letter}`); + const [data, update] = useCachedGetAPI(`/api/competitions/${compId}/problems/${id}`); + const [champData, champUpdate] = useCachedGetAPI(`/api/competitions/${compId}`); const [editorText, setEditorText] = useState(null) const [isLoading, setIsLoading] = useState(false); useEffect(() => { update(); + champUpdate(); }, []); @@ -32,14 +35,21 @@ const SeeProblemPage = () => { setEditorText(formData.src) // alert(JSON.stringify(data)) - const defaultLang = Object.keys(data.langs)[0] - if (formData.cars === ""){ - formData.cars = data.langs[defaultLang] + const defaultLang = champData?.checkers[0]?.id + if (formData.cars === "") { + formData.cars = defaultLang } console.log(defaultLang) - axios.post('/api/send', formData) + const config = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` + } + } + + axios.post(`/api/competitions/${compId}/send`, formData, config) .then(() => { setTimeout(() => { navigate("/sends") @@ -57,8 +67,8 @@ const SeeProblemPage = () => {
-

Задача {letter}

-

{data.name}

+

Задача {'A'}

+

{data.problem?.name}

@@ -79,13 +89,13 @@ const SeeProblemPage = () => {

Задача

- +

Входные данные

- +

Выходные данные

- +
@@ -118,23 +128,27 @@ const SeeProblemPage = () => {

Отправить решение

Вставьте код здесь

- + diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index a26553d..4cd3948 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -1,19 +1,22 @@ -import SeeQuizzProblemPage from "./pages/SeeQuizzProblemPage.jsx"; +import SeeQuizzProblemPage from "./pages/user/SeeQuizzProblemPage.jsx"; import("../node_modules/bootstrap/dist/js/bootstrap.min.js") import Header from "./components/Header.jsx"; -import ProblemsListPage from "./pages/ProblemsListPage.jsx"; -import SendsListPage from "./pages/SendsListPage.jsx"; -import StatsPage from "./pages/StatsPage.jsx"; -import SeeProblemPage from "./pages/SeeProblemPage.jsx"; +import ProblemsListPage from "./pages/user/ProblemsListPage.jsx"; +import SendsListPage from "./pages/user/SendsListPage.jsx"; +import StatsPage from "./pages/user/StatsPage.jsx"; +import SeeProblemPage from "./pages/user/SeeProblemPage.jsx"; -import SeeSendPage from "./pages/SeeSendPage.jsx"; -import StatusesPage from "./pages/StatusesPage.jsx"; +import SeeSendPage from "./pages/user/SeeSendPage.jsx"; +import StatusesPage from "./pages/user/StatusesPage.jsx"; import {BrowserRouter, Route, Routes} from "react-router-dom"; -import LoginPage from "./pages/LoginPage.jsx"; -import ChampsPage from "./pages/ChampsPage.jsx"; +import LoginPage from "./pages/user/LoginPage.jsx"; +import ChampsPage from "./pages/user/ChampsPage.jsx"; +import {AdminChampsPage} from "./pages/admin/AdminChampsPage.jsx"; +import {AdminChampsDetailPage} from "./pages/admin/AdminChampsDetailPage.jsx"; +import {AdminChampsDetailCheckerPage} from "./pages/admin/AdminChampsDetailCheckersPage.jsx"; function App() { @@ -34,6 +37,10 @@ function App() { }/> }/> }/> + + }/> + }/> + }/> diff --git a/FRONTEND_V2/src/components/Header.jsx b/FRONTEND_V2/src/components/Header.jsx index 97f865d..ae4948a 100644 --- a/FRONTEND_V2/src/components/Header.jsx +++ b/FRONTEND_V2/src/components/Header.jsx @@ -34,7 +34,10 @@ const Header = () => { */} - - Отмена + + Отмена diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx index e2314a5..04f2056 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx @@ -6,6 +6,7 @@ import {Link, useParams} from "react-router-dom"; import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; export const AdminChampsDetailPage = () => { @@ -27,18 +28,14 @@ export const AdminChampsDetailPage = () => { - - - - - - + +

Управление

- Пользователи - Задания + Пользователи + Задания @@ -71,7 +68,7 @@ export const AdminChampsDetailPage = () => { Время конца соревнования
- +

Чекеры

diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx index c904b11..ac5d2d3 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx @@ -6,6 +6,7 @@ import {Link} from "react-router-dom"; import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; export const AdminChampsPage = () => { @@ -24,11 +25,8 @@ export const AdminChampsPage = () => { - - - - - + + { data?.map(elem => { diff --git a/FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx b/FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx new file mode 100644 index 0000000..0f29cd8 --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx @@ -0,0 +1,41 @@ +import Card from "../../components/bootstrap/Card.jsx"; +import ResponsiveTable from "../../components/bootstrap/ResponsiveTable.jsx"; +import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import {useEffect} from "react"; +import {Link} from "react-router-dom"; +import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; + +export const AdminCheckersPage = () => { + + const [data, update] = useCachedGetAPI("/api/checkers",() => {}, []); + + useEffect(() => { + update() + }, []); + + console.log(data) + + return ( + <> + + + + + + + + + { + data?.map(elem => { + return +

{elem.displayName} | {elem.languageHighlightName}

+ Удалить +
+ }) + } + + ); +}; diff --git a/FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx b/FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx new file mode 100644 index 0000000..0962976 --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx @@ -0,0 +1,43 @@ +import Card from "../../components/bootstrap/Card.jsx"; +import ResponsiveTable from "../../components/bootstrap/ResponsiveTable.jsx"; +import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import {useEffect} from "react"; +import {Link} from "react-router-dom"; +import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; + +export const AdminProblemsPage = () => { + + const [data, update] = useCachedGetAPI("/api/problems",() => {}, []); + + useEffect(() => { + update() + }, []); + + console.log(data) + + return ( + <> + + + + + + + + + { + data?.map(elem => { + return + {/*
Идет
*/} +

{elem.name}

+

{elem.description}

+ Управлять +
+ }) + } + + ); +}; diff --git a/FRONTEND_V2/src/utils/consts.js b/FRONTEND_V2/src/utils/consts.js index a0055dd..5cbe660 100644 --- a/FRONTEND_V2/src/utils/consts.js +++ b/FRONTEND_V2/src/utils/consts.js @@ -1,5 +1,6 @@ const constants = { LOCALSTORAGE_AUTH_KEY: "__authed", + LOCALSTORAGE_IS_ADMIN_KEY: "__admin", LOCALSTORAGE_LAST_LANG_KEY: "__lang", LOCALSTORAGE_JWT: "__token", } From 9b885c55578e9ed3bd981d44d48eb59e2e914a04 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Tue, 7 Jan 2025 19:27:21 +0300 Subject: [PATCH 026/117] backend_2: add profile --- .../ru/codebattles/backend/entity/User.kt | 5 ++++- .../web/controllers/UsersController.kt | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt index 76e194c..82539a1 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt @@ -16,7 +16,10 @@ data class User( var mpassword: String?, ) : UserDetails, BaseEntity() { override fun getAuthorities(): MutableCollection { - return mutableListOf(SimpleGrantedAuthority("ROLE_USER")) + return mutableListOf( + SimpleGrantedAuthority("ROLE_USER"), + SimpleGrantedAuthority("ROLE_ADMIN"), + ) } override fun getPassword() = mpassword diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt new file mode 100644 index 0000000..f0a76a2 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt @@ -0,0 +1,20 @@ +package ru.codebattles.backend.web.controllers + +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import ru.codebattles.backend.entity.User +import ru.codebattles.backend.repository.UserRepository +import java.util.* + +@RestController +@RequestMapping("/api/users") +class UsersController( + val userRepository: UserRepository, +) { + @GetMapping("me") + fun getProfile(@AuthenticationPrincipal user: User): Optional { + return userRepository.findById(user.id!!) + } +} From 72740890cfb01b8d0adafdb1464bfc8d5845e717 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Tue, 7 Jan 2025 19:49:32 +0300 Subject: [PATCH 027/117] backend_2: add jwt config --- .../ru/codebattles/backend/BackendV2Application.kt | 3 +++ .../backend/core/properties/JwtTokenProperties.kt | 8 ++++++++ .../kotlin/ru/codebattles/backend/services/JwtService.kt | 9 ++++++--- BACKEND_V2/src/main/resources/application.properties | 2 ++ 4 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/properties/JwtTokenProperties.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/BackendV2Application.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/BackendV2Application.kt index 72c06ca..623d2d3 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/BackendV2Application.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/BackendV2Application.kt @@ -1,9 +1,12 @@ package ru.codebattles.backend import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.runApplication +import ru.codebattles.backend.core.properties.JwtTokenProperties @SpringBootApplication +@EnableConfigurationProperties(JwtTokenProperties::class) class BackendV2Application fun main(args: Array) { diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/properties/JwtTokenProperties.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/properties/JwtTokenProperties.kt new file mode 100644 index 0000000..987a7c4 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/properties/JwtTokenProperties.kt @@ -0,0 +1,8 @@ +package ru.codebattles.backend.core.properties + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties("codebattles.jwt") +data class JwtTokenProperties ( + val secretKey: String, +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt index 3754e34..49902d6 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt @@ -4,20 +4,23 @@ package ru.codebattles.backend.services import io.jsonwebtoken.Jwts import io.jsonwebtoken.security.Keys import org.springframework.stereotype.Service +import ru.codebattles.backend.core.properties.JwtTokenProperties import java.security.Key import java.util.* import javax.crypto.SecretKey @Service -class JwtService { +class JwtService( + private val properties: JwtTokenProperties +) { private val jwtSecret: Key = getSecretKey() private val jwtExpirationMs = 3600000 // 1 hour private final fun getSecretKey(): SecretKey { - val base64Key = "alexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalex" - val decodedKey = Base64.getDecoder().decode(base64Key) + val secretKey = properties.secretKey + val decodedKey = Base64.getDecoder().decode(secretKey) return Keys.hmacShaKeyFor(decodedKey) } diff --git a/BACKEND_V2/src/main/resources/application.properties b/BACKEND_V2/src/main/resources/application.properties index 9366a92..d67603d 100644 --- a/BACKEND_V2/src/main/resources/application.properties +++ b/BACKEND_V2/src/main/resources/application.properties @@ -4,3 +4,5 @@ spring.datasource.username=postgres spring.datasource.password=admin spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect + +codebattles.jwt.secret-key=alexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalex From 6c8cdc2398df6d3095446080528fda33c2b4ed1f Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 10 Jan 2025 19:43:00 +0300 Subject: [PATCH 028/117] backend_2: add competitions security (If u not member you can`t interact with competition) --- .../annotations/CompetitionAccessRequired.kt | 5 +++ .../aspects/CompetitionAccessAspect.kt | 30 +++++++++++++ .../ru/codebattles/backend/entity/User.kt | 7 +++- .../backend/repository/AnswerRepository.kt | 2 + .../repository/CompetitionRepository.kt | 1 + .../backend/services/AnswerService.kt | 5 +++ .../web/controllers/CompetitionsController.kt | 42 ++++++++++++------- 7 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/annotations/CompetitionAccessRequired.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/aspects/CompetitionAccessAspect.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/annotations/CompetitionAccessRequired.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/annotations/CompetitionAccessRequired.kt new file mode 100644 index 0000000..54c1807 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/annotations/CompetitionAccessRequired.kt @@ -0,0 +1,5 @@ +package ru.codebattles.backend.annotations + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class CompetitionAccessRequired diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/aspects/CompetitionAccessAspect.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/aspects/CompetitionAccessAspect.kt new file mode 100644 index 0000000..488726d --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/aspects/CompetitionAccessAspect.kt @@ -0,0 +1,30 @@ +package ru.codebattles.backend.aspects + +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Before +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.stereotype.Component +import ru.codebattles.backend.entity.User +import ru.codebattles.backend.repository.CompetitionRepository +import java.nio.file.AccessDeniedException + +@Aspect +@Component +class CompetitionAccessAspect( + private val competitionRepository: CompetitionRepository, +) { + @Before("@annotation(ru.codebattles.backend.annotations.CompetitionAccessRequired) && args(compId, ..)") + fun checkAccess(compId: Long) { + val authentication = SecurityContextHolder.getContext().authentication + if (authentication == null || !authentication.isAuthenticated) { + throw IllegalStateException("User is not authenticated!") + } + + val user: User = authentication.principal as User + +// if (user.isAdmin()) return + if (competitionRepository.existsByIdAndMembersId(compId, user.id!!)) return + + throw AccessDeniedException("User does not have access to this competition") + } +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt index 82539a1..208c7b2 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt @@ -18,7 +18,7 @@ data class User( override fun getAuthorities(): MutableCollection { return mutableListOf( SimpleGrantedAuthority("ROLE_USER"), - SimpleGrantedAuthority("ROLE_ADMIN"), +// SimpleGrantedAuthority("ROLE_ADMIN"), ) } @@ -26,4 +26,9 @@ data class User( override fun getUsername() = musername + fun isAdmin(): Boolean { + return authorities.contains(SimpleGrantedAuthority("ROLE_ADMIN")) + } + + } \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt index b4823ee..7826393 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt @@ -6,4 +6,6 @@ import ru.codebattles.backend.entity.User interface AnswerRepository : JpaRepository { fun getAllByUserAndCompetitionId(user: User, compId: Long): List + fun getAllByUserIdAndCompetitionId(userId: Long, compId: Long): List + } \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt index 4187903..d74c2cb 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionRepository.kt @@ -6,4 +6,5 @@ import ru.codebattles.backend.entity.User interface CompetitionRepository : JpaRepository { fun getByMembersContaining(user: User): List + fun existsByIdAndMembersId(id: Long, memberId: Long): Boolean } \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt index e50c929..f1aa274 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt @@ -55,6 +55,11 @@ class AnswerService( answerRepository.getAllByUserAndCompetitionId(user, competition) ) } + fun getAllAnswersByCompetitionsAndUserId(competition: Long, userId: Long): List { + return answerMapper.toDtoS( + answerRepository.getAllByUserIdAndCompetitionId(userId, competition) + ) + } fun getById(id: Long): AnswerDto { return answerMapper.toDto( diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt index ddc584e..4458ba0 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt @@ -4,11 +4,13 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement import org.springframework.beans.factory.annotation.Autowired import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.* +import ru.codebattles.backend.annotations.CompetitionAccessRequired import ru.codebattles.backend.dto.AnswerDto import ru.codebattles.backend.dto.CompetitionCreateDto import ru.codebattles.backend.dto.CompetitionDto import ru.codebattles.backend.dto.CompetitionsProblemsDto -import ru.codebattles.backend.entity.* +import ru.codebattles.backend.entity.Leaderboard +import ru.codebattles.backend.entity.User import ru.codebattles.backend.services.AnswerService import ru.codebattles.backend.services.CompetitionService import ru.codebattles.backend.web.entity.SendAnswerRequest @@ -29,14 +31,16 @@ class CompetitionsController { return competitionService.create(instance, user) } - @GetMapping("{id}") - fun getById(@PathVariable id: Long): CompetitionDto { - return competitionService.getById(id) + @CompetitionAccessRequired + @GetMapping("{compId}") + fun getById(@PathVariable compId: Long): CompetitionDto { + return competitionService.getById(compId) } - @PostMapping("{id}/send") + @CompetitionAccessRequired + @PostMapping("{compId}/send") fun send( - @PathVariable id: Long, + @PathVariable compId: Long, @AuthenticationPrincipal user: User, @RequestBody data: SendAnswerRequest ): String { @@ -44,27 +48,35 @@ class CompetitionsController { return "aboba" } - @GetMapping("{id}/sends") - fun getAnswers(@PathVariable id: Long, @AuthenticationPrincipal user: User): List { - return answerService.getAllAnswersByCompetitionsAndUser(id, user); + @CompetitionAccessRequired + @GetMapping("{compId}/sends") + fun getAnswers( + @PathVariable compId: Long, + @AuthenticationPrincipal user: User, + ): List { + return answerService.getAllAnswersByCompetitionsAndUserId(compId, user.id!!); } - @GetMapping("{id}/problems") - fun getProblemsByCompetition(@PathVariable id: Long): List { - return competitionService.getProblemsById(id) + @CompetitionAccessRequired + @GetMapping("{compId}/problems") + fun getProblemsByCompetition(@PathVariable compId: Long): List { + return competitionService.getProblemsById(compId) } - @GetMapping("{id}/leaderboard") - fun leaderboard(@PathVariable id: Long): Leaderboard { - return competitionService.getLeaderboardById(id) + @CompetitionAccessRequired + @GetMapping("{compId}/leaderboard") + fun leaderboard(@PathVariable compId: Long): Leaderboard { + return competitionService.getLeaderboardById(compId) } + @CompetitionAccessRequired @GetMapping("{compId}/problems/{id}") fun getProblemsByIdByCompetition(@PathVariable compId: Long, @PathVariable id: Long): CompetitionsProblemsDto { return competitionService.getProblemById(compId, id) } + @CompetitionAccessRequired @GetMapping("/me") fun getAllAvaliableForUser(@AuthenticationPrincipal user: User): List { return competitionService.getAllByUser(user) From 32c0d33956ecf262a08d34ac8dd8ba2710332f73 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 10 Jan 2025 19:43:52 +0300 Subject: [PATCH 029/117] frontend_2: automatic api url --- FRONTEND_V2/src/utils/settings.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/FRONTEND_V2/src/utils/settings.js b/FRONTEND_V2/src/utils/settings.js index b28a218..5b57664 100644 --- a/FRONTEND_V2/src/utils/settings.js +++ b/FRONTEND_V2/src/utils/settings.js @@ -1,10 +1,6 @@ import axios from "axios"; -// axios.defaults.withCredentials = true - -const isProd = import.meta.env.VITE_ENV === "production"; -if (!isProd) { - // axios.defaults.baseURL =`${document.location.protocol}//${document.location.hostname}:2500` +const productionBuild = import.meta.env.VITE_ENV === "production"; +if (!productionBuild) { + axios.defaults.baseURL = `${document.location.protocol}//${document.location.hostname}:${8080}` } - -axios.defaults.baseURL =`${document.location.protocol}//${document.location.hostname}:${8080}` From cc55033e862b91e487a794e21069f9dc56554540 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:29:49 +0300 Subject: [PATCH 030/117] backend_2: add lastSend endpoint --- .../backend/repository/AnswerRepository.kt | 1 + .../codebattles/backend/services/AnswerService.kt | 7 +++++++ .../backend/web/controllers/AnswerController.kt | 13 +++++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt index 7826393..546431c 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/AnswerRepository.kt @@ -7,5 +7,6 @@ import ru.codebattles.backend.entity.User interface AnswerRepository : JpaRepository { fun getAllByUserAndCompetitionId(user: User, compId: Long): List fun getAllByUserIdAndCompetitionId(userId: Long, compId: Long): List + fun getFirstByUserIdAndCompetitionsProblemsIdOrderByCreatedAtDesc(userId: Long, compProbId: Long): Answer } \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt index f1aa274..22449d6 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/AnswerService.kt @@ -55,12 +55,19 @@ class AnswerService( answerRepository.getAllByUserAndCompetitionId(user, competition) ) } + fun getAllAnswersByCompetitionsAndUserId(competition: Long, userId: Long): List { return answerMapper.toDtoS( answerRepository.getAllByUserIdAndCompetitionId(userId, competition) ) } + fun getLastByUserIdAndCompetitionsAnswerId(userId: Long, competitionProblemId: Long): AnswerDto { + return answerMapper.toDto( + answerRepository.getFirstByUserIdAndCompetitionsProblemsIdOrderByCreatedAtDesc(userId, competitionProblemId) + ) + } + fun getById(id: Long): AnswerDto { return answerMapper.toDto( answerRepository.findById(id).orElseThrow() diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/AnswerController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/AnswerController.kt index ce47dc1..d778cc9 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/AnswerController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/AnswerController.kt @@ -1,10 +1,7 @@ package ru.codebattles.backend.web.controllers import io.swagger.v3.oas.annotations.security.SecurityRequirement -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.* import ru.codebattles.backend.dto.AnswerDto import ru.codebattles.backend.services.AnswerService @@ -18,4 +15,12 @@ class AnswerController( fun getById(@PathVariable id: Long): AnswerDto { return answerService.getById(id) } + + @GetMapping("/last") + fun getLastSendByProblemAnswerAndUserId( + @RequestParam compProblemId: Long, + @RequestParam userId: Long, + ): AnswerDto { + return answerService.getLastByUserIdAndCompetitionsAnswerId(userId, compProblemId) + } } From d128253a0b653fe5476e9f08d2657abc7694580a Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:21:17 +0300 Subject: [PATCH 031/117] backend_2: add default admin perm --- .../src/main/kotlin/ru/codebattles/backend/entity/User.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt index 208c7b2..a05b8bd 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt @@ -18,7 +18,7 @@ data class User( override fun getAuthorities(): MutableCollection { return mutableListOf( SimpleGrantedAuthority("ROLE_USER"), -// SimpleGrantedAuthority("ROLE_ADMIN"), + SimpleGrantedAuthority("ROLE_ADMIN"), ) } From c5f7ec8049ae1e76acd5073889d490e3353edbc9 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:21:32 +0300 Subject: [PATCH 032/117] frontend_2: add admin scoreboard --- FRONTEND_V2/src/App.jsx | 4 + .../src/pages/admin/AdminChampsDetailPage.jsx | 1 + .../admin/AdminChampsDetailRatingPage.jsx | 116 ++++++++++++++++++ .../src/pages/admin/AdminSeeSendPage.jsx | 87 +++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 FRONTEND_V2/src/pages/admin/AdminChampsDetailRatingPage.jsx create mode 100644 FRONTEND_V2/src/pages/admin/AdminSeeSendPage.jsx diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index 00f6626..ce876d9 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -19,6 +19,8 @@ import {AdminChampsDetailPage} from "./pages/admin/AdminChampsDetailPage.jsx"; import {AdminChampsDetailCheckerPage} from "./pages/admin/AdminChampsDetailCheckersPage.jsx"; import {AdminProblemsPage} from "./pages/admin/AdminProblemsPage.jsx"; import {AdminCheckersPage} from "./pages/admin/AdminCheckersPage.jsx"; +import {AdminChampsDetailRatingPage} from "./pages/admin/AdminChampsDetailRatingPage.jsx"; +import {AdminSeeSendPage} from "./pages/admin/AdminSeeSendPage.jsx"; function App() { @@ -45,6 +47,8 @@ function App() { }/> }/> }/> + }/> + }/> diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx index 04f2056..1ed63b6 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx @@ -36,6 +36,7 @@ export const AdminChampsDetailPage = () => { Пользователи Задания + Рейтинг diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailRatingPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsDetailRatingPage.jsx new file mode 100644 index 0000000..f7c6483 --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/AdminChampsDetailRatingPage.jsx @@ -0,0 +1,116 @@ +import Card from "../../components/bootstrap/Card.jsx"; +import ResponsiveTable from "../../components/bootstrap/ResponsiveTable.jsx"; +import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import {useEffect} from "react"; +import {Link, useParams} from "react-router-dom"; +import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; +import {formatDate} from "../../utils/format.js"; +import {getCookie} from "../../utils/cookies.js"; + +export const AdminChampsDetailRatingPage = () => { + + const {compId} = useParams() + + const mine_user_id = getCookie("user_id") + + const [data, update] = useCachedGetAPI(`/api/competitions/${compId}/leaderboard`); + const [problemsData, problrmsUpdate] = useCachedGetAPI(`/api/competitions/${compId}/problems`, null, []); + + useEffect(() => { + update(); + problrmsUpdate() + }, []); + + + useEffect(() => { + update() + }, []); + + console.log(data) + + return ( + <> + + + + + + + + + +

Рейтинг

+
+ + + + + № + Пользователь + { + problemsData?.map(compProb => { + return + {compProb.slug} + + + }) + } + Всего + Посл. Посылка + + + + { + data?.score?.map((scoreRow, i) => { + const groupedAnswers = Object.groupBy( + data.data[scoreRow.userId], + ({competitionproblemId}) => competitionproblemId + ); + + // const groupedAnswers = {} + // const user = 0 + + // console.log(user) + console.log(data.score) + + return ( + + {i + 1} + {scoreRow.userId} + {/*-1*/} + { + problemsData?.map(compProb => { + + console.log(compProb) + + return + + {groupedAnswers[compProb.id]?.[0]?.maxScore} + + + }) + } + {scoreRow.score} + {formatDate(scoreRow.time)} + + ) + }) + } + + +
+ +
+ + ); +}; diff --git a/FRONTEND_V2/src/pages/admin/AdminSeeSendPage.jsx b/FRONTEND_V2/src/pages/admin/AdminSeeSendPage.jsx new file mode 100644 index 0000000..9345c21 --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/AdminSeeSendPage.jsx @@ -0,0 +1,87 @@ +import {useEffect} from 'react'; +import Card from "../../components/bootstrap/Card.jsx"; +import {useParams, useSearchParams} from "react-router-dom"; +import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import LazySyntaxHighlight from "../../components/lazy/LazySyntaxHightlight.jsx"; +import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import ResponsiveTable from "../../components/bootstrap/ResponsiveTable.jsx"; +import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; +import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; + +export const AdminSeeSendPage = () => { + + const {compId} = useParams(); + + const [searchParams] = useSearchParams(); + + const [data, update] = useCachedGetAPI(`/api/answers/last?compProblemId=${searchParams.get("compprobId")}&userId=${searchParams.get("userId")}`); + + const tests = JSON.parse(data?.result || "{}") + + console.log(tests) + + useEffect(() => { + update() + }, []); + + const colorsByResult = { + "WRONG_ANSWER": "table-danger", + "SUCCESS": "table-success", + "RUNTIME_ERROR": "table-info", + "COMPILATION_ERROR": "table-info", + "TIME_LIMIT": "table-warning", + "NOT_EXECUTED": "table-active", + } + + return (<> + + + {/**/} + {/* */} + {/* */} + {/* */} + {/**/} + + +

Анализ посылки

+

Язык: {data?.checker?.displayName}

+ Исходный код: + + {data.code} + +
+ + + + № Теста + Время (ms) + Вердикт + Вывод + + + + { + tests?.results?.map((test, i) => { + return ( + + {i + 1} + {test.time} + {test.msg} +

{test.out}

+ + ) + }) + } + {/**/} + {/* -1*/} + {/* -*/} + {/* Подробные тесты будут доступны позже*/} + {/*

-

*/} + {/**/} + +
+
+ + ); +}; + From c01dc34dc0d845268cf75622f7c9fb6018a2e945 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Tue, 21 Jan 2025 22:40:13 +0300 Subject: [PATCH 033/117] frontend_2: add 404 page --- FRONTEND_V2/src/App.jsx | 3 +++ FRONTEND_V2/src/pages/NotFound.jsx | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 FRONTEND_V2/src/pages/NotFound.jsx diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index ce876d9..e6cc900 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -21,6 +21,7 @@ import {AdminProblemsPage} from "./pages/admin/AdminProblemsPage.jsx"; import {AdminCheckersPage} from "./pages/admin/AdminCheckersPage.jsx"; import {AdminChampsDetailRatingPage} from "./pages/admin/AdminChampsDetailRatingPage.jsx"; import {AdminSeeSendPage} from "./pages/admin/AdminSeeSendPage.jsx"; +import {NotFound} from "./pages/NotFound.jsx"; function App() { @@ -49,6 +50,8 @@ function App() { }/> }/> }/> + + }/> diff --git a/FRONTEND_V2/src/pages/NotFound.jsx b/FRONTEND_V2/src/pages/NotFound.jsx new file mode 100644 index 0000000..d9a79c0 --- /dev/null +++ b/FRONTEND_V2/src/pages/NotFound.jsx @@ -0,0 +1,13 @@ +import Card from "../components/bootstrap/Card.jsx"; +import {Link} from "react-router-dom"; + +export const NotFound = () => { + return ( + +

Упс....

+

Такой страницы не найдено(

+ На главную +
+ ); +}; + From 4ed0bbb09e31a00f53637ea192ac0ee3cc0300b1 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sun, 2 Feb 2025 21:26:42 +0300 Subject: [PATCH 034/117] backend: admin api for user management --- .../backend/dto/mapper/UserMapper.kt | 13 ++++++++++++ .../backend/dto/mapper/core/AbstractMapper.kt | 1 + .../codebattles/backend/entity/Competition.kt | 2 +- .../backend/repository/UserRepository.kt | 1 + .../backend/services/CompetitionService.kt | 21 ++++++++++++++++++- .../web/controllers/CompetitionsController.kt | 19 +++++++++++++---- .../backend/web/entity/EditUsersRequest.kt | 5 +++++ .../web/entity/ProblemCreateRequest.kt | 11 ++++++++++ 8 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserMapper.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/EditUsersRequest.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/ProblemCreateRequest.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserMapper.kt new file mode 100644 index 0000000..c18d32b --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserMapper.kt @@ -0,0 +1,13 @@ +package ru.codebattles.backend.dto.mapper + +import org.mapstruct.Mapper +import ru.codebattles.backend.dto.AnswerDto +import ru.codebattles.backend.dto.ProblemDto +import ru.codebattles.backend.dto.UserDto +import ru.codebattles.backend.dto.mapper.core.AbstractMapper +import ru.codebattles.backend.entity.Answer +import ru.codebattles.backend.entity.Problem +import ru.codebattles.backend.entity.User + +@Mapper(componentModel = "spring") +interface UserMapper : AbstractMapper diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt index d4861e9..80c043a 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/core/AbstractMapper.kt @@ -6,4 +6,5 @@ interface AbstractMapper { fun fromDto(obj: DTO): OBJ fun fromDtoS(obj: List): List fun toDtoS(obj: List): List + fun toDtoS(obj: Set): List } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt index 0b39bc9..d3f9093 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt @@ -7,7 +7,7 @@ import java.util.* @Table(name = "competitions") data class Competition( @ManyToMany - val members: MutableSet? = mutableSetOf(), + var members: MutableSet? = mutableSetOf(), @ManyToMany val checkers: MutableSet? = mutableSetOf(), diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt index 8a9b806..564cb74 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt @@ -8,4 +8,5 @@ import ru.codebattles.backend.entity.User @Repository interface UserRepository : JpaRepository { fun findByMusername(username: String): User + fun findByIdIn(ids: Set): MutableSet } \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt index 3aef714..44075c4 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt @@ -6,21 +6,25 @@ import org.springframework.web.server.ResponseStatusException import ru.codebattles.backend.dto.CompetitionCreateDto import ru.codebattles.backend.dto.CompetitionDto import ru.codebattles.backend.dto.CompetitionsProblemsDto +import ru.codebattles.backend.dto.UserDto import ru.codebattles.backend.dto.mapper.CompetitionsCreateMapper import ru.codebattles.backend.dto.mapper.CompetitionsMapper import ru.codebattles.backend.dto.mapper.CompetitionsProblemsMapper -import ru.codebattles.backend.entity.CompetitionsProblems +import ru.codebattles.backend.dto.mapper.UserMapper import ru.codebattles.backend.entity.LeaderBoardAllTasksQuery import ru.codebattles.backend.entity.Leaderboard import ru.codebattles.backend.entity.User import ru.codebattles.backend.repository.CompetitionProblemsRepository import ru.codebattles.backend.repository.CompetitionRepository import ru.codebattles.backend.repository.TestRepo +import ru.codebattles.backend.repository.UserRepository import java.util.stream.Collectors @Service class CompetitionService( private val competitionRepository: CompetitionRepository, + private val userRepository: UserRepository, + private val userMapper: UserMapper, private val competitionProblemsRepository: CompetitionProblemsRepository, private val competitionsProblemsMapper: CompetitionsProblemsMapper, private val competitionsMapper: CompetitionsMapper, @@ -70,6 +74,21 @@ class CompetitionService( ) } + fun patchUsers(compId: Long, usersIds: Set){ + val competition = competitionRepository.findById(compId).orElseThrow() + competition.members = userRepository.findByIdIn(usersIds) + + competitionRepository.save(competition) + } + + fun getUsers(compId: Long): List { + val competition = competitionRepository.findById(compId).orElseThrow() + + return userMapper.toDtoS(competition.members!!) + } + + + fun getAllByUser(user: User): List { return competitionsMapper.toDtoS( competitionRepository.getByMembersContaining(user) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt index 4458ba0..aeba6ea 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt @@ -2,17 +2,16 @@ package ru.codebattles.backend.web.controllers import io.swagger.v3.oas.annotations.security.SecurityRequirement import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.HttpStatus import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.* import ru.codebattles.backend.annotations.CompetitionAccessRequired -import ru.codebattles.backend.dto.AnswerDto -import ru.codebattles.backend.dto.CompetitionCreateDto -import ru.codebattles.backend.dto.CompetitionDto -import ru.codebattles.backend.dto.CompetitionsProblemsDto +import ru.codebattles.backend.dto.* import ru.codebattles.backend.entity.Leaderboard import ru.codebattles.backend.entity.User import ru.codebattles.backend.services.AnswerService import ru.codebattles.backend.services.CompetitionService +import ru.codebattles.backend.web.entity.EditUsersRequest import ru.codebattles.backend.web.entity.SendAnswerRequest @@ -70,6 +69,18 @@ class CompetitionsController { return competitionService.getLeaderboardById(compId) } + + @PutMapping("{compId}/users") + @ResponseStatus(HttpStatus.ACCEPTED) + fun editUsers(@PathVariable compId: Long, @RequestBody data: EditUsersRequest) { + competitionService.patchUsers(compId, data.usersIds) + } + + @GetMapping("{compId}/users") + fun getUsers(@PathVariable compId: Long): List { + return competitionService.getUsers(compId) + } + @CompetitionAccessRequired @GetMapping("{compId}/problems/{id}") fun getProblemsByIdByCompetition(@PathVariable compId: Long, @PathVariable id: Long): CompetitionsProblemsDto { diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/EditUsersRequest.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/EditUsersRequest.kt new file mode 100644 index 0000000..7984f0b --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/EditUsersRequest.kt @@ -0,0 +1,5 @@ +package ru.codebattles.backend.web.entity + +data class EditUsersRequest( + val usersIds: Set +) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/ProblemCreateRequest.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/ProblemCreateRequest.kt new file mode 100644 index 0000000..368679e --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/ProblemCreateRequest.kt @@ -0,0 +1,11 @@ +package ru.codebattles.backend.web.entity + +data class ProblemCreateRequest( + val name: String, + val description: String, + val inData: String, + val outData: String, + val tests: String, + val examples: String, + val public: Boolean? = false +) \ No newline at end of file From 182b55780dff44690365ea98df8fd61ccb7507e0 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:40:28 +0300 Subject: [PATCH 035/117] frontend_2: add users editing --- FRONTEND_V2/package.json | 1 + FRONTEND_V2/src/App.jsx | 2 + .../src/pages/admin/AdminChampsDetailPage.jsx | 2 +- .../src/pages/admin/AdminUsersDetailPage.jsx | 116 ++++++++++++++++++ FRONTEND_V2/yarn.lock | 103 ++++++++++++++++ 5 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx diff --git a/FRONTEND_V2/package.json b/FRONTEND_V2/package.json index 6b2c1c8..24d8c7e 100644 --- a/FRONTEND_V2/package.json +++ b/FRONTEND_V2/package.json @@ -16,6 +16,7 @@ "bootstrap": "5.3.3", "bootstrap-icons": "^1.11.3", "dompurify": "^3.1.3", + "rc-select": "^14.16.6", "react": "^18.2.0", "react-ace": "^10.1.0", "react-dom": "^18.2.0", diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index e6cc900..e0f346b 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -22,6 +22,7 @@ import {AdminCheckersPage} from "./pages/admin/AdminCheckersPage.jsx"; import {AdminChampsDetailRatingPage} from "./pages/admin/AdminChampsDetailRatingPage.jsx"; import {AdminSeeSendPage} from "./pages/admin/AdminSeeSendPage.jsx"; import {NotFound} from "./pages/NotFound.jsx"; +import {AdminUsersDetailPage} from "./pages/admin/AdminUsersDetailPage.jsx"; function App() { @@ -47,6 +48,7 @@ function App() { }/> }/> }/> + }/> }/> }/> }/> diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx index 1ed63b6..bf388a3 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx @@ -34,7 +34,7 @@ export const AdminChampsDetailPage = () => {

Управление

- Пользователи + Пользователи Задания Рейтинг diff --git a/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx b/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx new file mode 100644 index 0000000..45bda2e --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx @@ -0,0 +1,116 @@ +import Card from "../../components/bootstrap/Card.jsx"; +import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import {useEffect, useState} from "react"; +import {Link, useNavigate, useParams} from "react-router-dom"; +import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; + +import Select, {Option} from 'rc-select'; +import 'rc-select/assets/index.css'; + +import {useForm} from "react-hook-form"; +import axios from "axios"; +import constants from "../../utils/consts.js"; + +export const AdminUsersDetailPage = () => { + + const {compId} = useParams() + + const [data, update] = useCachedGetAPI(`/api/competitions/${compId}/users`, () => { + }, []); + const [users, updateUsers] = useCachedGetAPI(`/api/users`, () => { + }, []); + + const [selectesUsers, setSelectesUsers] = useState([]) + + useEffect(() => { + update() + }, []); + + // console.log(data) + + useEffect(() => { + setSelectesUsers(data.map(elem => elem.id)) + }, [data]) + + const handleSelectChange = (value) => { + setSelectesUsers(value); // Update the state with selected values + }; + + const { + register, + handleSubmit, + watch, + formState: {errors}, + } = useForm() + + const navigate = useNavigate() + + const [loading, setLoading] = useState(false) + + const onSubmit = () => { + setLoading(true) + axios + .put(`/api/competitions/${compId}/users`, + {usersIds: selectesUsers}, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` + } + }) + .then(() => { + navigate(`/admin/champs/${compId}/edit`) + }) + .catch(() => { + navigate("/") + + }) + .finally(() => { + setLoading(false) + }) + } + + return ( + <> + + + + + + + + + +

Управление участниками

+ +
+ +
+ +
+
+ + ); +}; diff --git a/FRONTEND_V2/yarn.lock b/FRONTEND_V2/yarn.lock index 7737a16..df603f7 100644 --- a/FRONTEND_V2/yarn.lock +++ b/FRONTEND_V2/yarn.lock @@ -180,6 +180,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.7.tgz#f4e7fe527cd710f8dc0618610b61b4b060c3c341" + integrity sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.3.1": version "7.24.1" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz" @@ -445,6 +452,27 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== +"@rc-component/portal@^1.1.0": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" + integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.24.4" + +"@rc-component/trigger@^2.1.1": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.6.tgz#bfe6602313b3fadd659687746511f813299d5ea4" + integrity sha512-/9zuTnWwhQ3S3WT1T8BubuFTT46kvnXgaERR9f4BTKyn61/wpf/BvbImzYBubzJibU707FxwbKszLlHjcLiv1Q== + dependencies: + "@babel/runtime" "^7.23.2" + "@rc-component/portal" "^1.1.0" + classnames "^2.3.2" + rc-motion "^2.0.0" + rc-resize-observer "^1.3.1" + rc-util "^5.44.0" + "@remix-run/router@1.15.3": version "1.15.3" resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz" @@ -922,6 +950,11 @@ character-reference-invalid@^2.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== +classnames@2.x, classnames@^2.2.1, classnames@^2.2.6, classnames@^2.3.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" @@ -2901,6 +2934,66 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +rc-motion@^2.0.0, rc-motion@^2.0.1: + version "2.9.5" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c" + integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.44.0" + +rc-overflow@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.4.1.tgz#e1bcf0375979c24cffa2d87bf83a19ded5fcdf45" + integrity sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-resize-observer "^1.0.0" + rc-util "^5.37.0" + +rc-resize-observer@^1.0.0, rc-resize-observer@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569" + integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ== + dependencies: + "@babel/runtime" "^7.20.7" + classnames "^2.2.1" + rc-util "^5.44.1" + resize-observer-polyfill "^1.5.1" + +rc-select@^14.16.6: + version "14.16.6" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.16.6.tgz#1c57a9aa97248b3fd9a830d9bf5df6e9d2ad2c69" + integrity sha512-YPMtRPqfZWOm2XGTbx5/YVr1HT0vn//8QS77At0Gjb3Lv+Lbut0IORJPKLWu1hQ3u4GsA0SrDzs7nI8JG7Zmyg== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^2.1.1" + classnames "2.x" + rc-motion "^2.0.1" + rc-overflow "^1.3.1" + rc-util "^5.16.1" + rc-virtual-list "^3.5.2" + +rc-util@^5.16.1, rc-util@^5.24.4, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.44.0, rc-util@^5.44.1: + version "5.44.3" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.3.tgz#9eca5039906446113c4032859f88c15234547961" + integrity sha512-q6KCcOFk3rv/zD3MckhJteZxb0VjAIFuf622B7ElK4vfrZdAzs16XR5p3VTdy3+U5jfJU5ACz4QnhLSuAGe5dA== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^18.2.0" + +rc-virtual-list@^3.5.2: + version "3.18.1" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.18.1.tgz#a081237b7e468eaec7e64a896a4261c3ed6f1c4b" + integrity sha512-ARSsD/dey/I4yNQHFYYUaKLUkD1wnD4lRZIvb3rCLMbTMmoFQJRVrWuSfbNt5P5MzMNooEBDvqrUPM4QN7BMNA== + dependencies: + "@babel/runtime" "^7.20.0" + classnames "^2.2.6" + rc-resize-observer "^1.0.0" + rc-util "^5.36.0" + react-ace@^10.1.0: version "10.1.0" resolved "https://registry.npmjs.org/react-ace/-/react-ace-10.1.0.tgz" @@ -2930,6 +3023,11 @@ react-is@^16.13.1: resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^18.2.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + react-markdown@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.0.1.tgz#c05ddbff67fd3b3f839f8c648e6fb35d022397d1" @@ -3081,6 +3179,11 @@ remark-stringify@^11.0.0: mdast-util-to-markdown "^2.0.0" unified "^11.0.0" +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" From 293f98fe0527d0b76be56341beb3a816984743ea Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:40:50 +0300 Subject: [PATCH 036/117] backend_2: admin access --- .../ru/codebattles/backend/aspects/CompetitionAccessAspect.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/aspects/CompetitionAccessAspect.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/aspects/CompetitionAccessAspect.kt index 488726d..713a64e 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/aspects/CompetitionAccessAspect.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/aspects/CompetitionAccessAspect.kt @@ -22,7 +22,7 @@ class CompetitionAccessAspect( val user: User = authentication.principal as User -// if (user.isAdmin()) return + if (user.isAdmin()) return if (competitionRepository.existsByIdAndMembersId(compId, user.id!!)) return throw AccessDeniedException("User does not have access to this competition") From b2578fb44b50988a26f04d1417e6ac1c97e1a5cf Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sun, 16 Feb 2025 16:31:58 +0300 Subject: [PATCH 037/117] backend_v2: get users --- .../backend/web/controllers/UsersController.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt index f0a76a2..c2d0397 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt @@ -1,5 +1,6 @@ package ru.codebattles.backend.web.controllers +import io.swagger.v3.oas.annotations.security.SecurityRequirement import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping @@ -10,6 +11,7 @@ import java.util.* @RestController @RequestMapping("/api/users") +@SecurityRequirement(name = "JWT") class UsersController( val userRepository: UserRepository, ) { @@ -17,4 +19,10 @@ class UsersController( fun getProfile(@AuthenticationPrincipal user: User): Optional { return userRepository.findById(user.id!!) } + + + @GetMapping + fun getAll(@AuthenticationPrincipal user: User): MutableList { + return userRepository.findAll() + } } From c9d1f1a00edc8bee93e9adac6d7e50fd10d5c997 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:23:34 +0300 Subject: [PATCH 038/117] backend_v2: add problems creating api --- .../backend/dto/CreateProblemDto.kt | 11 ++++++++++ .../dto/mapper/CreateProblemsMapper.kt | 9 ++++++++ .../backend/dto/mapper/ProblemsMapper.kt | 1 + .../ru/codebattles/backend/entity/Problem.kt | 21 +++++++++++++++---- .../backend/services/ProblemsService.kt | 20 +++++++++++++++--- .../web/controllers/ProblemsController.kt | 3 ++- 6 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CreateProblemDto.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CreateProblemsMapper.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CreateProblemDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CreateProblemDto.kt new file mode 100644 index 0000000..2f8229e --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CreateProblemDto.kt @@ -0,0 +1,11 @@ +package ru.codebattles.backend.dto + + +data class CreateProblemDto( + val name: String, + val description: String, + val inData: String, + val outData: String, + val tests: String, + val examples: String, +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CreateProblemsMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CreateProblemsMapper.kt new file mode 100644 index 0000000..4794ce1 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CreateProblemsMapper.kt @@ -0,0 +1,9 @@ +package ru.codebattles.backend.dto.mapper + +import org.mapstruct.Mapper +import ru.codebattles.backend.dto.CreateProblemDto +import ru.codebattles.backend.dto.mapper.core.AbstractMapper +import ru.codebattles.backend.entity.Problem + +@Mapper(componentModel = "spring") +interface CreateProblemsMapper : AbstractMapper diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/ProblemsMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/ProblemsMapper.kt index bcebcc4..6d7faf6 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/ProblemsMapper.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/ProblemsMapper.kt @@ -1,6 +1,7 @@ package ru.codebattles.backend.dto.mapper import org.mapstruct.Mapper +import ru.codebattles.backend.dto.CreateProblemDto import ru.codebattles.backend.dto.ProblemDto import ru.codebattles.backend.dto.mapper.core.AbstractMapper import ru.codebattles.backend.entity.Problem diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt index c62ec5f..a71ac30 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Problem.kt @@ -1,14 +1,27 @@ package ru.codebattles.backend.entity -import jakarta.persistence.Entity +import jakarta.persistence.* + @Entity data class Problem( + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = 0, + val name: String, + + @Column(name = "description", columnDefinition = "TEXT") val description: String, + + @Column(name = "in_data", columnDefinition = "TEXT") val inData: String, + + @Column(name = "out_data", columnDefinition = "TEXT") val outData: String, + + @Column(name = "tests", columnDefinition = "TEXT") val tests: String, - val examples: String, - val public: Boolean? = false -) : BaseEntity() \ No newline at end of file + + @Column(name = "examples", columnDefinition = "TEXT") + val examples: String +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt index 29bc32e..2b5a8f1 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt @@ -3,14 +3,18 @@ package ru.codebattles.backend.services import org.springframework.http.HttpStatus import org.springframework.stereotype.Service import org.springframework.web.server.ResponseStatusException +import ru.codebattles.backend.dto.CreateProblemDto import ru.codebattles.backend.dto.ProblemDto +import ru.codebattles.backend.dto.mapper.CreateProblemsMapper import ru.codebattles.backend.dto.mapper.ProblemsMapper +import ru.codebattles.backend.entity.Problem import ru.codebattles.backend.repository.ProblemsRepository @Service class ProblemsService( val problemsRepository: ProblemsRepository, val problemsMapper: ProblemsMapper, + val createProblemsMapper: CreateProblemsMapper ) { fun getById(id: Long): ProblemDto { val optionalProblem = problemsRepository.findById(id) @@ -23,9 +27,19 @@ class ProblemsService( } - fun create(problemDto: ProblemDto): ProblemDto { - val convertedFromDto = problemsMapper.fromDto(problemDto) - val competition = problemsRepository.save(convertedFromDto) + fun create(problemDto: CreateProblemDto): ProblemDto { + val problem = Problem( + name = problemDto.name, + description = problemDto.description, + inData = problemDto.inData, + outData = problemDto.outData, + tests = problemDto.tests, + examples = problemDto.examples, + ) + + println() + + val competition = problemsRepository.save(problem) println() diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProblemsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProblemsController.kt index 478f8b1..57b89e4 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProblemsController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProblemsController.kt @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement import org.springframework.beans.factory.annotation.Autowired import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.* +import ru.codebattles.backend.dto.CreateProblemDto import ru.codebattles.backend.dto.ProblemDto import ru.codebattles.backend.entity.User import ru.codebattles.backend.services.ProblemsService @@ -17,7 +18,7 @@ class ProblemsController { private lateinit var problemsService: ProblemsService @PostMapping - fun create(@RequestBody instance: ProblemDto, @AuthenticationPrincipal user: User): ProblemDto { + fun create(@RequestBody instance: CreateProblemDto, @AuthenticationPrincipal user: User): ProblemDto { return problemsService.create(instance) } From fd1bc6a6836e4a85e7631cc78ab4f7602bfcc961 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:08:54 +0300 Subject: [PATCH 039/117] backend_v2: add default roles and default user --- .../DefaultAdminCommandRunner.kt | 50 +++++++++++++++++++ .../DefaultRolesCommandRunner.kt | 28 +++++++++++ .../ru/codebattles/backend/entity/Role.kt | 14 ++++++ .../ru/codebattles/backend/entity/User.kt | 14 +++--- .../ru/codebattles/backend/entity/Variable.kt | 17 +++++++ .../backend/repository/RolesRepository.kt | 9 ++++ .../backend/repository/UserRepository.kt | 1 + .../backend/repository/VariablesRepository.kt | 10 ++++ 8 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/commandrunner/DefaultAdminCommandRunner.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/commandrunner/DefaultRolesCommandRunner.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Role.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Variable.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/RolesRepository.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/VariablesRepository.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/commandrunner/DefaultAdminCommandRunner.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/commandrunner/DefaultAdminCommandRunner.kt new file mode 100644 index 0000000..34a8a7f --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/commandrunner/DefaultAdminCommandRunner.kt @@ -0,0 +1,50 @@ +package ru.codebattles.backend.core.commandrunner + +import org.springframework.boot.CommandLineRunner +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Component +import ru.codebattles.backend.entity.Role +import ru.codebattles.backend.entity.User +import ru.codebattles.backend.entity.Variable +import ru.codebattles.backend.repository.RolesRepository +import ru.codebattles.backend.repository.UserRepository +import ru.codebattles.backend.repository.VariablesRepository + +@Component +class DefaultAdminCommandRunner( + val variablesRepository: VariablesRepository, + val userRepository: UserRepository, + val rolesRepository: RolesRepository, + val passwordEncoder: PasswordEncoder, +) : CommandLineRunner { + + val VARIABLE_KEY: String = "DEFAULT_USER_EXECUTOR_COMPLETE" + + val adminUserDefaultRolesList: List = listOf( + "ROLE_ADMIN", + ) + + override fun run(vararg args: String?) { + val notFirstExecute = variablesRepository.existsByKey(VARIABLE_KEY) + val userWithUsernameAdminExists = userRepository.existsByMusername("admin") + + if (notFirstExecute) { + return + } + + if (!userWithUsernameAdminExists) { + val user = User(mpassword = passwordEncoder.encode("admin"), musername = "admin") + + user.roles.addAll( + adminUserDefaultRolesList.map { roleName -> + rolesRepository.findByName(roleName) ?: Role(name = roleName) + } + ) + + userRepository.save(user) + + } + + variablesRepository.save(Variable(key = VARIABLE_KEY, value = "true")) + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/commandrunner/DefaultRolesCommandRunner.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/commandrunner/DefaultRolesCommandRunner.kt new file mode 100644 index 0000000..bfc46eb --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/commandrunner/DefaultRolesCommandRunner.kt @@ -0,0 +1,28 @@ +package ru.codebattles.backend.core.commandrunner + +import lombok.extern.java.Log +import org.springframework.boot.CommandLineRunner +import org.springframework.core.Ordered +import org.springframework.core.annotation.Order +import org.springframework.stereotype.Component +import ru.codebattles.backend.entity.Role +import ru.codebattles.backend.repository.RolesRepository + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +class DefaultRolesCommandRunner( + val rolesRepository: RolesRepository +) : CommandLineRunner { + + val defaultRolesList: List = listOf( + "ROLE_USER", "ROLE_ADMIN", + ) + + override fun run(vararg args: String?) { + for (role in defaultRolesList) { + if (!rolesRepository.existsByName(role)) { + rolesRepository.save(Role(name = role)) + } + } + } +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Role.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Role.kt new file mode 100644 index 0000000..2389c78 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Role.kt @@ -0,0 +1,14 @@ +package ru.codebattles.backend.entity + + +import jakarta.persistence.* + +@Entity +@Table(name = "roles") +data class Role( + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = 0, + + @Column(name = "name", unique = true, nullable = false) + val name: String +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt index a05b8bd..99dc56f 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt @@ -1,8 +1,7 @@ package ru.codebattles.backend.entity -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.Table + +import jakarta.persistence.* import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.core.userdetails.UserDetails @@ -14,12 +13,13 @@ data class User( var musername: String?, @Column(name = "password") var mpassword: String?, + + @ManyToMany(fetch = FetchType.EAGER) + var roles: MutableSet = mutableSetOf(), + ) : UserDetails, BaseEntity() { override fun getAuthorities(): MutableCollection { - return mutableListOf( - SimpleGrantedAuthority("ROLE_USER"), - SimpleGrantedAuthority("ROLE_ADMIN"), - ) + return roles.map { SimpleGrantedAuthority(it.name) }.toMutableList() } override fun getPassword() = mpassword diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Variable.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Variable.kt new file mode 100644 index 0000000..491d132 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Variable.kt @@ -0,0 +1,17 @@ +package ru.codebattles.backend.entity + + +import jakarta.persistence.* + +@Entity +@Table(name = "variables") +data class Variable( + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = 0, + + @Column(name = "name", unique = true, nullable = false) + val key: String, + + @Column(name = "value", nullable = true) + val value: String?, +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/RolesRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/RolesRepository.kt new file mode 100644 index 0000000..f3585c1 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/RolesRepository.kt @@ -0,0 +1,9 @@ +package ru.codebattles.backend.repository + +import org.springframework.data.jpa.repository.JpaRepository +import ru.codebattles.backend.entity.Role + +interface RolesRepository : JpaRepository { + fun findByName(name: String): Role? + fun existsByName(name: String): Boolean +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt index 564cb74..d83acac 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/UserRepository.kt @@ -9,4 +9,5 @@ import ru.codebattles.backend.entity.User interface UserRepository : JpaRepository { fun findByMusername(username: String): User fun findByIdIn(ids: Set): MutableSet + fun existsByMusername(username: String): Boolean } \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/VariablesRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/VariablesRepository.kt new file mode 100644 index 0000000..251d55a --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/VariablesRepository.kt @@ -0,0 +1,10 @@ +package ru.codebattles.backend.repository + +import org.springframework.data.jpa.repository.JpaRepository +import ru.codebattles.backend.entity.Variable + + +interface VariablesRepository : JpaRepository { + fun findByKey(key: String): Variable? + fun existsByKey(key: String): Boolean +} \ No newline at end of file From 7e948577c2da0698925b04aed48ed92a7afb6e4d Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 1 Mar 2025 11:05:32 +0300 Subject: [PATCH 040/117] FRONTEND_V2: fix lint --- FRONTEND_V2/.eslintrc.cjs | 20 - FRONTEND_V2/eslint.config.js | 19 + FRONTEND_V2/package-lock.json | 1983 ++++++++++++++++++++++++++++++++- FRONTEND_V2/package.json | 8 +- FRONTEND_V2/yarn.lock | 1727 +++++++++++++++++----------- 5 files changed, 3096 insertions(+), 661 deletions(-) delete mode 100644 FRONTEND_V2/.eslintrc.cjs create mode 100644 FRONTEND_V2/eslint.config.js diff --git a/FRONTEND_V2/.eslintrc.cjs b/FRONTEND_V2/.eslintrc.cjs deleted file mode 100644 index 6df9d7e..0000000 --- a/FRONTEND_V2/.eslintrc.cjs +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], - rules: { - 'react/jsx-no-target-blank': 'off', - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} diff --git a/FRONTEND_V2/eslint.config.js b/FRONTEND_V2/eslint.config.js new file mode 100644 index 0000000..90b1592 --- /dev/null +++ b/FRONTEND_V2/eslint.config.js @@ -0,0 +1,19 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import pluginReact from "eslint-plugin-react"; + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + { + files: ["src/**/*.{js,mjs,cjs,jsx}"], + languageOptions: { globals: globals.browser } + }, + pluginJs.configs.recommended, + pluginReact.configs.flat.recommended, + { + files: ["src/**/*.{js,mjs,cjs,jsx}"], + rules: { + "react/react-in-jsx-scope": "off" + } + } +]; \ No newline at end of file diff --git a/FRONTEND_V2/package-lock.json b/FRONTEND_V2/package-lock.json index c4a25dd..7f72795 100644 --- a/FRONTEND_V2/package-lock.json +++ b/FRONTEND_V2/package-lock.json @@ -8,22 +8,30 @@ "name": "frontend-v2", "version": "0.0.0", "dependencies": { + "@popperjs/core": "^2.11.8", "ace-builds": "^1.32.9", "axios": "^1.6.8", "bootstrap": "5.3.3", + "bootstrap-icons": "^1.11.3", + "dompurify": "^3.1.3", + "rc-select": "^14.16.6", "react": "^18.2.0", "react-ace": "^10.1.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.2", + "react-markdown": "^9.0.1", "react-router-dom": "^6.22.3", - "react-syntax-highlighter": "^15.5.0" + "react-syntax-highlighter": "^15.5.0", + "rehype-raw": "^7.0.0", + "remark-gemoji": "^8.0.0", + "remark-gfm": "^4.0.0", + "usehooks-ts": "^3.1.0" }, "devDependencies": { "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "@vitejs/plugin-react": "^4.2.1", "eslint": "^8.57.0", - "eslint-plugin-no-comments": "^1.1.10", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", @@ -726,12 +734,48 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rc-component/portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz", + "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/trigger": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.6.tgz", + "integrity": "sha512-/9zuTnWwhQ3S3WT1T8BubuFTT46kvnXgaERR9f4BTKyn61/wpf/BvbImzYBubzJibU707FxwbKszLlHjcLiv1Q==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@rc-component/portal": "^1.1.0", + "classnames": "^2.3.2", + "rc-motion": "^2.0.0", + "rc-resize-observer": "^1.3.1", + "rc-util": "^5.44.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/@remix-run/router": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", @@ -800,13 +844,28 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true, "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/hast": { "version": "2.3.10", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", @@ -815,18 +874,29 @@ "@types/unist": "^2" } }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "dev": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.2.73", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.73.tgz", "integrity": "sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==", - "dev": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -843,6 +913,12 @@ "@types/react": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "node_modules/@types/unist": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", @@ -852,7 +928,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true, "license": "ISC" }, "node_modules/@vitejs/plugin-react": { @@ -1131,6 +1206,15 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1157,6 +1241,21 @@ "@popperjs/core": "^2.11.8" } }, + "node_modules/bootstrap-icons": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz", + "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ] + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1260,6 +1359,15 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1286,6 +1394,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/character-entities-legacy": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", @@ -1304,6 +1421,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1377,7 +1499,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, "license": "MIT" }, "node_modules/data-view-buffer": { @@ -1438,7 +1559,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -1452,6 +1572,27 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decode-named-character-reference/node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1503,6 +1644,26 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/diff-match-patch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", @@ -1521,6 +1682,14 @@ "node": ">=0.10.0" } }, + "node_modules/dompurify": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.719", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.719.tgz", @@ -1528,6 +1697,17 @@ "dev": true, "license": "ISC" }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.23.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", @@ -1818,12 +1998,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-plugin-no-comments": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/eslint-plugin-no-comments/-/eslint-plugin-no-comments-1.1.10.tgz", - "integrity": "sha512-hJohtfKNKDDAmhQ/VsvaN7Q41npFVBeAQISf54ywRQx7EhknF+068SPHj5g3njKMoxYAuk62ahJvJQQvMYeL7g==", - "dev": true - }, "node_modules/eslint-plugin-react": { "version": "7.34.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", @@ -1977,6 +2151,15 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -1987,6 +2170,11 @@ "node": ">=0.10.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2178,6 +2366,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gemoji": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/gemoji/-/gemoji-8.1.0.tgz", + "integrity": "sha512-HA4Gx59dw2+tn+UAa7XEV4ufUKI4fH1KgcbenVA9YKSj1QJTT0xh5Mwv5HMFNN3l2OtUe3ZIfuRwSyZS5pLIWw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2400,6 +2597,93 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-from-parse5/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/hast-util-from-parse5/node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/property-information": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz", + "integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-from-parse5/node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/hast-util-parse-selector": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", @@ -2409,6 +2693,182 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-raw/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.5.tgz", + "integrity": "sha512-gHD+HoFxOMmmXLuq9f2dZDMQHVcplCVpMfBNRpJsF03yyLZvJGzsFORe8orVuYDX9k2w0VH0uF8oryFd1whqKQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/property-information": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz", + "integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-to-parse5/node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-parse5/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-parse5/node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/hastscript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", @@ -2433,6 +2893,24 @@ "node": "*" } }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -2498,6 +2976,11 @@ "dev": true, "license": "ISC" }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==" + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -2778,8 +3261,19 @@ "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.1.4", + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, @@ -3065,6 +3559,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "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", @@ -3082,6 +3581,15 @@ "dev": true, "license": "MIT" }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3129,6 +3637,944 @@ "node": ">=12" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/mdast-util-mdx-jsx/node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3165,7 +4611,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -3409,6 +4854,17 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3569,6 +5025,107 @@ ], "license": "MIT" }, + "node_modules/rc-motion": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz", + "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.44.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-overflow": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.4.1.tgz", + "integrity": "sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.37.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-resize-observer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz", + "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==", + "dependencies": { + "@babel/runtime": "^7.20.7", + "classnames": "^2.2.1", + "rc-util": "^5.44.1", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-select": { + "version": "14.16.6", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.6.tgz", + "integrity": "sha512-YPMtRPqfZWOm2XGTbx5/YVr1HT0vn//8QS77At0Gjb3Lv+Lbut0IORJPKLWu1hQ3u4GsA0SrDzs7nI8JG7Zmyg==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.1.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-overflow": "^1.3.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-util": { + "version": "5.44.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz", + "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/rc-virtual-list": { + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.18.3.tgz", + "integrity": "sha512-s1/bZQY2uwnmgXYeXxJkk2cSTz1cdUPDCrxAq/y1WQM115HilFFIvLi+JVFfkD4xCq3TZxGM17FQH4NLesWfwg==", + "dependencies": { + "@babel/runtime": "^7.20.0", + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -3631,6 +5188,40 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/react-markdown": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz", + "integrity": "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-markdown/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -3756,6 +5347,117 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-gemoji": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/remark-gemoji/-/remark-gemoji-8.0.0.tgz", + "integrity": "sha512-/fL9rc72FYwFGtOKcT+QeQdx9Q9t5v4N6KLXSDOTEgaedzK85I9judBqB2eqz+g4b0ERMejlwSOuPK+wket6aA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "gemoji": "^8.0.0", + "mdast-util-find-and-replace": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -4123,6 +5825,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities/node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -4149,6 +5873,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4220,6 +5952,24 @@ "node": ">=4" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4339,6 +6089,117 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-visit/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -4380,6 +6241,74 @@ "punycode": "^2.1.0" } }, + "node_modules/usehooks-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz", + "integrity": "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/vfile/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, "node_modules/vite": { "version": "5.2.6", "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz", @@ -4450,6 +6379,15 @@ "vite": "^4 || ^5" } }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4583,6 +6521,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/FRONTEND_V2/package.json b/FRONTEND_V2/package.json index 24d8c7e..30c4c87 100644 --- a/FRONTEND_V2/package.json +++ b/FRONTEND_V2/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "vite", "build": "vite build", - "lint": "eslint . --ext js,jsx --report-unused-disable-directives", + "lint": "eslint --config eslint.config.js src/", "preview": "vite preview" }, "dependencies": { @@ -30,13 +30,15 @@ "usehooks-ts": "^3.1.0" }, "devDependencies": { + "@eslint/js": "^9.21.0", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "@vitejs/plugin-react": "^4.2.1", - "eslint": "^8.57.0", - "eslint-plugin-react": "^7.34.1", + "eslint": "^9.21.0", + "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", + "globals": "^16.0.0", "vite": "^5.2.0", "vite-plugin-chunk-split": "^0.5.0" } diff --git a/FRONTEND_V2/yarn.lock b/FRONTEND_V2/yarn.lock index df603f7..f4933d6 100644 --- a/FRONTEND_V2/yarn.lock +++ b/FRONTEND_V2/yarn.lock @@ -180,14 +180,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2": - version "7.26.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.7.tgz#f4e7fe527cd710f8dc0618610b61b4b060c3c341" - integrity sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/runtime@^7.3.1": +"@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2", "@babel/runtime@^7.3.1": version "7.24.1" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz" integrity sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ== @@ -350,49 +343,87 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.6.1": - version "4.10.0" - resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz" - integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa" + integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w== + dependencies: + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/core@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.12.0.tgz#5f960c3d57728be9f6c65bd84aa6aa613078798e" + integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg== + dependencies: + "@types/json-schema" "^7.0.15" -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== +"@eslint/eslintrc@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.0.tgz#96a558f45842989cca7ea1ecd785ad5491193846" + integrity sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" + espree "^10.0.1" + globals "^14.0.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.57.0": - version "8.57.0" - resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz" - integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@eslint/js@9.21.0", "@eslint/js@^9.21.0": + version "9.21.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.21.0.tgz#4303ef4e07226d87c395b8fad5278763e9c15c08" + integrity sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw== -"@humanwhocodes/config-array@^0.11.14": - version "0.11.14" - resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.2.7": + version "0.2.7" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz#9901d52c136fb8f375906a73dcc382646c3b6a27" + integrity sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g== dependencies: - "@humanwhocodes/object-schema" "^2.0.2" - debug "^4.3.1" - minimatch "^3.0.5" + "@eslint/core" "^0.12.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.2": - version "2.0.2" - resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz" - integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.2.tgz#1860473de7dfa1546767448f333db80cb0ff2161" + integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ== "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" @@ -426,35 +457,14 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5": - version "2.0.5" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - "@popperjs/core@^2.11.8": version "2.11.8" - resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== "@rc-component/portal@^1.1.0": version "1.1.2" - resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" + resolved "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz" integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== dependencies: "@babel/runtime" "^7.18.0" @@ -463,7 +473,7 @@ "@rc-component/trigger@^2.1.1": version "2.2.6" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.6.tgz#bfe6602313b3fadd659687746511f813299d5ea4" + resolved "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.6.tgz" integrity sha512-/9zuTnWwhQ3S3WT1T8BubuFTT46kvnXgaERR9f4BTKyn61/wpf/BvbImzYBubzJibU707FxwbKszLlHjcLiv1Q== dependencies: "@babel/runtime" "^7.23.2" @@ -588,14 +598,14 @@ "@types/debug@^4.0.0": version "4.1.12" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz" integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== dependencies: "@types/ms" "*" "@types/estree-jsx@^1.0.0": version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" + resolved "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz" integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== dependencies: "@types/estree" "*" @@ -605,6 +615,11 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/hast@^2.0.0": version "2.3.10" resolved "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz" @@ -614,22 +629,27 @@ "@types/hast@^3.0.0": version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + resolved "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz" integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== dependencies: "@types/unist" "*" +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/mdast@^4.0.0": version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" + resolved "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz" integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== dependencies: "@types/unist" "*" "@types/ms@*": - version "0.7.34" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" - integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + version "2.1.0" + resolved "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz" + integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== "@types/prop-types@*": version "15.7.12" @@ -651,17 +671,27 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/unist@*", "@types/unist@^3.0.0": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.2.tgz#6dd61e43ef60b34086287f83683a5c1b2dc53d20" - integrity sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ== +"@types/trusted-types@^2.0.7": + version "2.0.7" + resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@types/unist@^2", "@types/unist@^2.0.0": +"@types/unist@*", "@types/unist@^2": version "2.0.10" resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz" integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== -"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0": +"@types/unist@^2.0.0": + version "2.0.11" + resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz" + integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== + +"@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + +"@ungap/structured-clone@^1.0.0": version "1.2.0" resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== @@ -687,10 +717,10 @@ acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.9.0: - version "8.11.3" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== ajv@^6.12.4: version "6.12.6" @@ -702,11 +732,6 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" @@ -734,7 +759,15 @@ array-buffer-byte-length@^1.0.1: call-bind "^1.0.5" is-array-buffer "^3.0.4" -array-includes@^3.1.6, array-includes@^3.1.7: +array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" + +array-includes@^3.1.6, array-includes@^3.1.8: version "3.1.8" resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz" integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== @@ -746,9 +779,9 @@ array-includes@^3.1.6, array-includes@^3.1.7: get-intrinsic "^1.2.4" is-string "^1.0.7" -array.prototype.findlast@^1.2.4: +array.prototype.findlast@^1.2.5: version "1.2.5" - resolved "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== dependencies: call-bind "^1.0.7" @@ -768,35 +801,25 @@ array.prototype.flat@^1.3.1: es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz" - integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - -array.prototype.toreversed@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz" - integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA== +array.prototype.flatmap@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" + integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" -array.prototype.tosorted@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz" - integrity sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg== +array.prototype.tosorted@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== dependencies: - call-bind "^1.0.5" + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.22.3" - es-errors "^1.1.0" + es-abstract "^1.23.3" + es-errors "^1.3.0" es-shim-unscopables "^1.0.2" arraybuffer.prototype.slice@^1.0.3: @@ -813,6 +836,19 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + is-array-buffer "^3.0.4" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" @@ -836,7 +872,7 @@ axios@^1.6.8: bail@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" + resolved "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz" integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== balanced-match@^1.0.0: @@ -846,7 +882,7 @@ balanced-match@^1.0.0: bootstrap-icons@^1.11.3: version "1.11.3" - resolved "https://registry.yarnpkg.com/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz#03f9cb754ec005c52f9ee616e2e84a82cab3084b" + resolved "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz" integrity sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww== bootstrap@5.3.3: @@ -872,6 +908,14 @@ browserslist@^4.22.2: node-releases "^2.0.14" update-browserslist-db "^1.0.13" +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" @@ -883,6 +927,24 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.2, call-bound@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.3.tgz#41cfd032b593e39176a71533ab4f384aa04fd681" + integrity sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA== + dependencies: + call-bind-apply-helpers "^1.0.1" + get-intrinsic "^1.2.6" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" @@ -895,7 +957,7 @@ caniuse-lite@^1.0.30001587: ccount@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz" integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== chalk@^2.4.2: @@ -917,7 +979,7 @@ chalk@^4.0.0: character-entities-html4@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + resolved "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz" integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== character-entities-legacy@^1.0.0: @@ -927,7 +989,7 @@ character-entities-legacy@^1.0.0: character-entities-legacy@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz" integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== character-entities@^1.0.0: @@ -937,7 +999,7 @@ character-entities@^1.0.0: character-entities@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + resolved "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz" integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== character-reference-invalid@^1.0.0: @@ -947,12 +1009,12 @@ character-reference-invalid@^1.0.0: character-reference-invalid@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" + resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz" integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== classnames@2.x, classnames@^2.2.1, classnames@^2.2.6, classnames@^2.3.2: version "2.5.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== color-convert@^1.9.0: @@ -993,7 +1055,7 @@ comma-separated-tokens@^1.0.0: comma-separated-tokens@^2.0.0: version "2.0.3" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz" integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== concat-map@0.0.1: @@ -1006,10 +1068,10 @@ convert-source-map@^2.0.0: resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -1029,6 +1091,15 @@ data-view-buffer@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + data-view-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz" @@ -1038,6 +1109,15 @@ data-view-byte-length@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + data-view-byte-offset@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz" @@ -1047,6 +1127,15 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +data-view-byte-offset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-data-view "^1.0.1" + debug@^4.0.0, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" @@ -1056,7 +1145,7 @@ debug@^4.0.0, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2: decode-named-character-reference@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" + resolved "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz" integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== dependencies: character-entities "^2.0.0" @@ -1091,12 +1180,12 @@ delayed-stream@~1.0.0: dequal@^2.0.0: version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== devlop@^1.0.0, devlop@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + resolved "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz" integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== dependencies: dequal "^2.0.0" @@ -1113,29 +1202,90 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - dompurify@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.3.tgz#cfe3ce4232c216d923832f68f2aa18b2fb9bd223" - integrity sha512-5sOWYSNPaxz6o2MUPvtyxTTqR4D3L77pr5rUQoWgD5ROQtVIZQgJkXbo1DLlK3vj11YGw5+LnF4SYti4gZmwng== + version "3.2.4" + resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz" + integrity sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + +dunder-proto@^1.0.0, dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" electron-to-chromium@^1.4.668: version "1.4.719" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.719.tgz" integrity sha512-FbWy2Q2YgdFzkFUW/W5jBjE9dj+804+98E4Pup78JBPnbdb3pv6IneY2JCPKdeKLh3AOKHQeYf+KwLr7mxGh6Q== -entities@^4.4.0: +entities@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2: +es-abstract@^1.17.5, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9: + version "1.23.9" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.9.tgz#5b45994b7de78dada5c1bebf1379646b32b9d606" + integrity sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA== + dependencies: + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.2.7" + get-proto "^1.0.0" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" + is-callable "^1.2.7" + is-data-view "^1.0.2" + is-regex "^1.2.1" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.0" + math-intrinsics "^1.1.0" + object-inspect "^1.13.3" + object-keys "^1.1.1" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.3" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.18" + +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: version "1.23.2" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz" integrity sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w== @@ -1194,30 +1344,37 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" -es-errors@^1.1.0, es-errors@^1.2.1, es-errors@^1.3.0: +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-iterator-helpers@^1.0.17: - version "1.0.18" - resolved "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz" - integrity sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA== +es-iterator-helpers@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz#d1dd0f58129054c0ad922e6a9a1e65eef435fe75" + integrity sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" define-properties "^1.2.1" - es-abstract "^1.23.0" + es-abstract "^1.23.6" es-errors "^1.3.0" es-set-tostringtag "^2.0.3" function-bind "^1.1.2" - get-intrinsic "^1.2.4" - globalthis "^1.0.3" + get-intrinsic "^1.2.6" + globalthis "^1.0.4" + gopd "^1.2.0" has-property-descriptors "^1.0.2" - has-proto "^1.0.3" - has-symbols "^1.0.3" - internal-slot "^1.0.7" - iterator.prototype "^1.1.2" - safe-array-concat "^1.1.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + internal-slot "^1.1.0" + iterator.prototype "^1.1.4" + safe-array-concat "^1.1.3" es-module-lexer@^1.4.1: version "1.5.0" @@ -1231,6 +1388,13 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" +es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz" @@ -1240,6 +1404,16 @@ es-set-tostringtag@^2.0.3: has-tostringtag "^1.0.2" hasown "^2.0.1" +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz" @@ -1256,6 +1430,15 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + dependencies: + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" + esbuild@^0.20.1: version "0.20.2" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz" @@ -1302,7 +1485,7 @@ escape-string-regexp@^4.0.0: escape-string-regexp@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz" integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== eslint-plugin-react-hooks@^4.6.0: @@ -1315,100 +1498,101 @@ eslint-plugin-react-refresh@^0.4.6: resolved "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.6.tgz" integrity sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA== -eslint-plugin-react@^7.34.1: - version "7.34.1" - resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz" - integrity sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw== +eslint-plugin-react@^7.37.4: + version "7.37.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz#1b6c80b6175b6ae4b26055ae4d55d04c414c7181" + integrity sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ== dependencies: - array-includes "^3.1.7" - array.prototype.findlast "^1.2.4" - array.prototype.flatmap "^1.3.2" - array.prototype.toreversed "^1.1.2" - array.prototype.tosorted "^1.1.3" + array-includes "^3.1.8" + array.prototype.findlast "^1.2.5" + array.prototype.flatmap "^1.3.3" + array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" - es-iterator-helpers "^1.0.17" + es-iterator-helpers "^1.2.1" estraverse "^5.3.0" + hasown "^2.0.2" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.7" - object.fromentries "^2.0.7" - object.hasown "^1.1.3" - object.values "^1.1.7" + object.entries "^1.1.8" + object.fromentries "^2.0.8" + object.values "^1.2.1" prop-types "^15.8.1" resolve "^2.0.0-next.5" semver "^6.3.1" - string.prototype.matchall "^4.0.10" + string.prototype.matchall "^4.0.12" + string.prototype.repeat "^1.0.0" -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.3.0: version "3.4.3" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.57.0: - version "8.57.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" - integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.21.0: + version "9.21.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.21.0.tgz#b1c9c16f5153ff219791f627b94ab8f11f811591" + integrity sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.0" - "@humanwhocodes/config-array" "^0.11.14" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.19.2" + "@eslint/core" "^0.12.0" + "@eslint/eslintrc" "^3.3.0" + "@eslint/js" "9.21.0" + "@eslint/plugin-kit" "^0.2.7" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" - cross-spawn "^7.0.2" + cross-spawn "^7.0.6" debug "^4.3.2" - doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" + file-entry-cache "^8.0.0" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== dependencies: - acorn "^8.9.0" + acorn "^8.14.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" + eslint-visitor-keys "^4.2.0" -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" @@ -1426,7 +1610,7 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: estree-util-is-identifier-name@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" + resolved "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz" integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== esutils@^2.0.2: @@ -1436,7 +1620,7 @@ esutils@^2.0.2: extend@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: @@ -1454,13 +1638,6 @@ fast-levenshtein@^2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fastq@^1.6.0: - version "1.17.1" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz" - integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== - dependencies: - reusify "^1.0.4" - fault@^1.0.0: version "1.0.4" resolved "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz" @@ -1468,12 +1645,12 @@ fault@^1.0.0: dependencies: format "^0.2.0" -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: - flat-cache "^3.0.4" + flat-cache "^4.0.0" find-up@^5.0.0: version "5.0.0" @@ -1483,14 +1660,13 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" + keyv "^4.5.4" flatted@^3.2.9: version "3.3.1" @@ -1523,11 +1699,6 @@ format@^0.2.0: resolved "https://registry.npmjs.org/format/-/format-0.2.2.tgz" integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww== -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -1538,7 +1709,7 @@ function-bind@^1.1.2: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: +function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -1548,6 +1719,18 @@ function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: es-abstract "^1.22.1" functions-have-names "^1.2.3" +function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" + functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" @@ -1555,7 +1738,7 @@ functions-have-names@^1.2.3: gemoji@^8.0.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/gemoji/-/gemoji-8.1.0.tgz#3d47a26e569c51efa95198822a6f483d7a7ae600" + resolved "https://registry.npmjs.org/gemoji/-/gemoji-8.1.0.tgz" integrity sha512-HA4Gx59dw2+tn+UAa7XEV4ufUKI4fH1KgcbenVA9YKSj1QJTT0xh5Mwv5HMFNN3l2OtUe3ZIfuRwSyZS5pLIWw== gensync@^1.0.0-beta.2: @@ -1574,6 +1757,30 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.0, get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-symbol-description@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz" @@ -1583,6 +1790,15 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" @@ -1590,29 +1806,20 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0: - version "13.24.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-16.0.0.tgz#3d7684652c5c4fbd086ec82f9448214da49382d8" + integrity sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A== globalthis@^1.0.3: version "1.0.3" @@ -1621,6 +1828,14 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" @@ -1628,10 +1843,10 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" @@ -1660,11 +1875,23 @@ has-proto@^1.0.1, has-proto@^1.0.3: resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz" integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== + dependencies: + dunder-proto "^1.0.0" + has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" @@ -1680,15 +1907,15 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: function-bind "^1.1.2" hast-util-from-parse5@^8.0.0: - version "8.0.1" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz#654a5676a41211e14ee80d1b1758c399a0327651" - integrity sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ== + version "8.0.3" + resolved "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz" + integrity sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg== dependencies: "@types/hast" "^3.0.0" "@types/unist" "^3.0.0" devlop "^1.0.0" - hastscript "^8.0.0" - property-information "^6.0.0" + hastscript "^9.0.0" + property-information "^7.0.0" vfile "^6.0.0" vfile-location "^5.0.0" web-namespaces "^2.0.0" @@ -1700,15 +1927,15 @@ hast-util-parse-selector@^2.0.0: hast-util-parse-selector@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" + resolved "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz" integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== dependencies: "@types/hast" "^3.0.0" hast-util-raw@^9.0.0: - version "9.0.3" - resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-9.0.3.tgz#87ad66bdd7b1ceb166452bdab7dfb3e9ba640419" - integrity sha512-ICWvVOF2fq4+7CMmtCPD5CM4QKjPbHpPotE6+8tDooV0ZuyJVUzHsrNX+O5NaRbieTf0F7FfeBOMAwi6Td0+yQ== + version "9.1.0" + resolved "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz" + integrity sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw== dependencies: "@types/hast" "^3.0.0" "@types/unist" "^3.0.0" @@ -1725,9 +1952,9 @@ hast-util-raw@^9.0.0: zwitch "^2.0.0" hast-util-to-jsx-runtime@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz#3ed27caf8dc175080117706bf7269404a0aa4f7c" - integrity sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ== + version "2.3.5" + resolved "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.5.tgz" + integrity sha512-gHD+HoFxOMmmXLuq9f2dZDMQHVcplCVpMfBNRpJsF03yyLZvJGzsFORe8orVuYDX9k2w0VH0uF8oryFd1whqKQ== dependencies: "@types/estree" "^1.0.0" "@types/hast" "^3.0.0" @@ -1739,7 +1966,7 @@ hast-util-to-jsx-runtime@^2.0.0: mdast-util-mdx-expression "^2.0.0" mdast-util-mdx-jsx "^3.0.0" mdast-util-mdxjs-esm "^2.0.0" - property-information "^6.0.0" + property-information "^7.0.0" space-separated-tokens "^2.0.0" style-to-object "^1.0.0" unist-util-position "^5.0.0" @@ -1747,7 +1974,7 @@ hast-util-to-jsx-runtime@^2.0.0: hast-util-to-parse5@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz#477cd42d278d4f036bc2ea58586130f6f39ee6ed" + resolved "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz" integrity sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw== dependencies: "@types/hast" "^3.0.0" @@ -1760,7 +1987,7 @@ hast-util-to-parse5@^8.0.0: hast-util-whitespace@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" + resolved "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz" integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== dependencies: "@types/hast" "^3.0.0" @@ -1776,15 +2003,15 @@ hastscript@^6.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" -hastscript@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-8.0.0.tgz#4ef795ec8dee867101b9f23cc830d4baf4fd781a" - integrity sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw== +hastscript@^9.0.0: + version "9.0.1" + resolved "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz" + integrity sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w== dependencies: "@types/hast" "^3.0.0" comma-separated-tokens "^2.0.0" hast-util-parse-selector "^4.0.0" - property-information "^6.0.0" + property-information "^7.0.0" space-separated-tokens "^2.0.0" highlight.js@^10.4.1, highlight.js@~10.7.0: @@ -1793,13 +2020,13 @@ highlight.js@^10.4.1, highlight.js@~10.7.0: integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== html-url-attributes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.0.tgz#fc4abf0c3fb437e2329c678b80abb3c62cff6f08" - integrity sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow== + version "3.0.1" + resolved "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz" + integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== html-void-elements@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" + resolved "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz" integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== ignore@^5.2.0: @@ -1825,23 +2052,10 @@ imurmurhash@^0.1.4: resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inline-style-parser@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.3.tgz#e35c5fb45f3a83ed7849fe487336eb7efa25971c" - integrity sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g== +inline-style-parser@0.2.4: + version "0.2.4" + resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz" + integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== internal-slot@^1.0.7: version "1.0.7" @@ -1852,6 +2066,15 @@ internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" + is-alphabetical@^1.0.0: version "1.0.4" resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz" @@ -1859,7 +2082,7 @@ is-alphabetical@^1.0.0: is-alphabetical@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" + resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz" integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== is-alphanumerical@^1.0.0: @@ -1872,7 +2095,7 @@ is-alphanumerical@^1.0.0: is-alphanumerical@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" + resolved "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz" integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== dependencies: is-alphabetical "^2.0.0" @@ -1886,6 +2109,15 @@ is-array-buffer@^3.0.4: call-bind "^1.0.2" get-intrinsic "^1.2.1" +is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + is-async-function@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz" @@ -1900,6 +2132,13 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== + dependencies: + has-bigints "^1.0.2" + is-boolean-object@^1.1.0: version "1.1.2" resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" @@ -1908,6 +2147,14 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" @@ -1927,6 +2174,15 @@ is-data-view@^1.0.1: dependencies: is-typed-array "^1.1.13" +is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== + dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + is-typed-array "^1.1.13" + is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" @@ -1934,6 +2190,14 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" +is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + is-decimal@^1.0.0: version "1.0.4" resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz" @@ -1941,7 +2205,7 @@ is-decimal@^1.0.0: is-decimal@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" + resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz" integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== is-extglob@^2.1.1: @@ -1949,12 +2213,12 @@ is-extglob@^2.1.1: resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-finalizationregistry@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz" - integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== dependencies: - call-bind "^1.0.2" + call-bound "^1.0.3" is-generator-function@^1.0.10: version "1.0.10" @@ -1977,7 +2241,7 @@ is-hexadecimal@^1.0.0: is-hexadecimal@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" + resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz" integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== is-map@^2.0.3: @@ -1997,14 +2261,17 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" is-plain-obj@^4.0.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz" integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== is-regex@^1.1.4: @@ -2015,6 +2282,16 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + is-set@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz" @@ -2027,6 +2304,13 @@ is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: dependencies: call-bind "^1.0.7" +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== + dependencies: + call-bound "^1.0.3" + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" @@ -2034,6 +2318,14 @@ is-string@^1.0.5, is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" +is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" @@ -2041,6 +2333,15 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== + dependencies: + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" + is-typed-array@^1.1.13: version "1.1.13" resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz" @@ -2048,6 +2349,13 @@ is-typed-array@^1.1.13: dependencies: which-typed-array "^1.1.14" +is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" + is-weakmap@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz" @@ -2060,6 +2368,13 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakref@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== + dependencies: + call-bound "^1.0.3" + is-weakset@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz" @@ -2078,16 +2393,17 @@ isexe@^2.0.0: resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -iterator.prototype@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz" - integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== +iterator.prototype@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" + integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== dependencies: - define-properties "^1.2.1" - get-intrinsic "^1.2.1" - has-symbols "^1.0.3" - reflect.getprototypeof "^1.0.4" - set-function-name "^2.0.1" + define-data-property "^1.1.4" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.6" + get-proto "^1.0.0" + has-symbols "^1.1.0" + set-function-name "^2.0.2" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -2136,9 +2452,9 @@ json5@^2.2.3: object.assign "^4.1.4" object.values "^1.1.6" -keyv@^4.5.3: +keyv@^4.5.4: version "4.5.4" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: json-buffer "3.0.1" @@ -2160,7 +2476,7 @@ locate-path@^6.0.0: lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== lodash.get@^4.4.2: @@ -2180,7 +2496,7 @@ lodash.merge@^4.6.2: longest-streak@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" + resolved "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz" integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== loose-envify@^1.1.0, loose-envify@^1.4.0: @@ -2213,14 +2529,19 @@ magic-string@^0.30.5: "@jridgewell/sourcemap-codec" "^1.4.15" markdown-table@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" - integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== + version "3.0.4" + resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz" + integrity sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== mdast-util-find-and-replace@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz#a6fc7b62f0994e973490e45262e4bc07607b04e0" - integrity sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA== + version "3.0.2" + resolved "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz" + integrity sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg== dependencies: "@types/mdast" "^4.0.0" escape-string-regexp "^5.0.0" @@ -2228,9 +2549,9 @@ mdast-util-find-and-replace@^3.0.0: unist-util-visit-parents "^6.0.0" mdast-util-from-markdown@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz#52f14815ec291ed061f2922fd14d6689c810cb88" - integrity sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA== + version "2.0.2" + resolved "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz" + integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== dependencies: "@types/mdast" "^4.0.0" "@types/unist" "^3.0.0" @@ -2246,9 +2567,9 @@ mdast-util-from-markdown@^2.0.0: unist-util-stringify-position "^4.0.0" mdast-util-gfm-autolink-literal@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz#5baf35407421310a08e68c15e5d8821e8898ba2a" - integrity sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg== + version "2.0.1" + resolved "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz" + integrity sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ== dependencies: "@types/mdast" "^4.0.0" ccount "^2.0.0" @@ -2257,9 +2578,9 @@ mdast-util-gfm-autolink-literal@^2.0.0: micromark-util-character "^2.0.0" mdast-util-gfm-footnote@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz#25a1753c7d16db8bfd53cd84fe50562bd1e6d6a9" - integrity sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ== + version "2.1.0" + resolved "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz" + integrity sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ== dependencies: "@types/mdast" "^4.0.0" devlop "^1.1.0" @@ -2269,7 +2590,7 @@ mdast-util-gfm-footnote@^2.0.0: mdast-util-gfm-strikethrough@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz#d44ef9e8ed283ac8c1165ab0d0dfd058c2764c16" + resolved "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz" integrity sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== dependencies: "@types/mdast" "^4.0.0" @@ -2278,7 +2599,7 @@ mdast-util-gfm-strikethrough@^2.0.0: mdast-util-gfm-table@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz#7a435fb6223a72b0862b33afbd712b6dae878d38" + resolved "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz" integrity sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== dependencies: "@types/mdast" "^4.0.0" @@ -2289,7 +2610,7 @@ mdast-util-gfm-table@^2.0.0: mdast-util-gfm-task-list-item@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz#e68095d2f8a4303ef24094ab642e1047b991a936" + resolved "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz" integrity sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== dependencies: "@types/mdast" "^4.0.0" @@ -2298,9 +2619,9 @@ mdast-util-gfm-task-list-item@^2.0.0: mdast-util-to-markdown "^2.0.0" mdast-util-gfm@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz#3f2aecc879785c3cb6a81ff3a243dc11eca61095" - integrity sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw== + version "3.1.0" + resolved "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz" + integrity sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ== dependencies: mdast-util-from-markdown "^2.0.0" mdast-util-gfm-autolink-literal "^2.0.0" @@ -2311,9 +2632,9 @@ mdast-util-gfm@^3.0.0: mdast-util-to-markdown "^2.0.0" mdast-util-mdx-expression@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz#4968b73724d320a379110d853e943a501bfd9d87" - integrity sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw== + version "2.0.1" + resolved "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz" + integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== dependencies: "@types/estree-jsx" "^1.0.0" "@types/hast" "^3.0.0" @@ -2323,9 +2644,9 @@ mdast-util-mdx-expression@^2.0.0: mdast-util-to-markdown "^2.0.0" mdast-util-mdx-jsx@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.2.tgz#daae777c72f9c4a106592e3025aa50fb26068e1b" - integrity sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA== + version "3.2.0" + resolved "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz" + integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== dependencies: "@types/estree-jsx" "^1.0.0" "@types/hast" "^3.0.0" @@ -2337,13 +2658,12 @@ mdast-util-mdx-jsx@^3.0.0: mdast-util-to-markdown "^2.0.0" parse-entities "^4.0.0" stringify-entities "^4.0.0" - unist-util-remove-position "^5.0.0" unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" mdast-util-mdxjs-esm@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" + resolved "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz" integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== dependencies: "@types/estree-jsx" "^1.0.0" @@ -2355,16 +2675,16 @@ mdast-util-mdxjs-esm@^2.0.0: mdast-util-phrasing@^4.0.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" + resolved "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz" integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== dependencies: "@types/mdast" "^4.0.0" unist-util-is "^6.0.0" mdast-util-to-hast@^13.0.0: - version "13.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz#1ae54d903150a10fe04d59f03b2b95fd210b2124" - integrity sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA== + version "13.2.0" + resolved "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz" + integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" @@ -2377,30 +2697,31 @@ mdast-util-to-hast@^13.0.0: vfile "^6.0.0" mdast-util-to-markdown@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz#9813f1d6e0cdaac7c244ec8c6dabfdb2102ea2b4" - integrity sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ== + version "2.1.2" + resolved "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz" + integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== dependencies: "@types/mdast" "^4.0.0" "@types/unist" "^3.0.0" longest-streak "^3.0.0" mdast-util-phrasing "^4.0.0" mdast-util-to-string "^4.0.0" + micromark-util-classify-character "^2.0.0" micromark-util-decode-string "^2.0.0" unist-util-visit "^5.0.0" zwitch "^2.0.0" mdast-util-to-string@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" + resolved "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz" integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== dependencies: "@types/mdast" "^4.0.0" micromark-core-commonmark@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz#9a45510557d068605c6e9a80f282b2bb8581e43d" - integrity sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA== + version "2.0.3" + resolved "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz" + integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== dependencies: decode-named-character-reference "^1.0.0" devlop "^1.0.0" @@ -2420,9 +2741,9 @@ micromark-core-commonmark@^2.0.0: micromark-util-types "^2.0.0" micromark-extension-gfm-autolink-literal@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz#f1e50b42e67d441528f39a67133eddde2bbabfd9" - integrity sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg== + version "2.1.0" + resolved "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz" + integrity sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw== dependencies: micromark-util-character "^2.0.0" micromark-util-sanitize-uri "^2.0.0" @@ -2430,9 +2751,9 @@ micromark-extension-gfm-autolink-literal@^2.0.0: micromark-util-types "^2.0.0" micromark-extension-gfm-footnote@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz#91afad310065a94b636ab1e9dab2c60d1aab953c" - integrity sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg== + version "2.1.0" + resolved "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz" + integrity sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw== dependencies: devlop "^1.0.0" micromark-core-commonmark "^2.0.0" @@ -2444,9 +2765,9 @@ micromark-extension-gfm-footnote@^2.0.0: micromark-util-types "^2.0.0" micromark-extension-gfm-strikethrough@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz#6917db8e320da70e39ffbf97abdbff83e6783e61" - integrity sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw== + version "2.1.0" + resolved "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz" + integrity sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw== dependencies: devlop "^1.0.0" micromark-util-chunked "^2.0.0" @@ -2456,9 +2777,9 @@ micromark-extension-gfm-strikethrough@^2.0.0: micromark-util-types "^2.0.0" micromark-extension-gfm-table@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz#2cf3fe352d9e089b7ef5fff003bdfe0da29649b7" - integrity sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw== + version "2.1.1" + resolved "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz" + integrity sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg== dependencies: devlop "^1.0.0" micromark-factory-space "^2.0.0" @@ -2468,15 +2789,15 @@ micromark-extension-gfm-table@^2.0.0: micromark-extension-gfm-tagfilter@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz#f26d8a7807b5985fba13cf61465b58ca5ff7dc57" + resolved "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz" integrity sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== dependencies: micromark-util-types "^2.0.0" micromark-extension-gfm-task-list-item@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz#ee8b208f1ced1eb9fb11c19a23666e59d86d4838" - integrity sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw== + version "2.1.0" + resolved "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz" + integrity sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw== dependencies: devlop "^1.0.0" micromark-factory-space "^2.0.0" @@ -2486,7 +2807,7 @@ micromark-extension-gfm-task-list-item@^2.0.0: micromark-extension-gfm@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz#3e13376ab95dd7a5cfd0e29560dfe999657b3c5b" + resolved "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz" integrity sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== dependencies: micromark-extension-gfm-autolink-literal "^2.0.0" @@ -2499,18 +2820,18 @@ micromark-extension-gfm@^3.0.0: micromark-util-types "^2.0.0" micromark-factory-destination@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz#857c94debd2c873cba34e0445ab26b74f6a6ec07" - integrity sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz" + integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== dependencies: micromark-util-character "^2.0.0" micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" micromark-factory-label@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz#17c5c2e66ce39ad6f4fc4cbf40d972f9096f726a" - integrity sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz" + integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== dependencies: devlop "^1.0.0" micromark-util-character "^2.0.0" @@ -2518,17 +2839,17 @@ micromark-factory-label@^2.0.0: micromark-util-types "^2.0.0" micromark-factory-space@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz#5e7afd5929c23b96566d0e1ae018ae4fcf81d030" - integrity sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz" + integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== dependencies: micromark-util-character "^2.0.0" micromark-util-types "^2.0.0" micromark-factory-title@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz#726140fc77892af524705d689e1cf06c8a83ea95" - integrity sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz" + integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== dependencies: micromark-factory-space "^2.0.0" micromark-util-character "^2.0.0" @@ -2536,9 +2857,9 @@ micromark-factory-title@^2.0.0: micromark-util-types "^2.0.0" micromark-factory-whitespace@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz#9e92eb0f5468083381f923d9653632b3cfb5f763" - integrity sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz" + integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== dependencies: micromark-factory-space "^2.0.0" micromark-util-character "^2.0.0" @@ -2546,48 +2867,48 @@ micromark-factory-whitespace@^2.0.0: micromark-util-types "^2.0.0" micromark-util-character@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.0.tgz#31320ace16b4644316f6bf057531689c71e2aee1" - integrity sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ== + version "2.1.1" + resolved "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== dependencies: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" micromark-util-chunked@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz#e51f4db85fb203a79dbfef23fd41b2f03dc2ef89" - integrity sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz" + integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== dependencies: micromark-util-symbol "^2.0.0" micromark-util-classify-character@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz#8c7537c20d0750b12df31f86e976d1d951165f34" - integrity sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz" + integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== dependencies: micromark-util-character "^2.0.0" micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" micromark-util-combine-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz#75d6ab65c58b7403616db8d6b31315013bfb7ee5" - integrity sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz" + integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== dependencies: micromark-util-chunked "^2.0.0" micromark-util-types "^2.0.0" micromark-util-decode-numeric-character-reference@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz#2698bbb38f2a9ba6310e359f99fcb2b35a0d2bd5" - integrity sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ== + version "2.0.2" + resolved "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz" + integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== dependencies: micromark-util-symbol "^2.0.0" micromark-util-decode-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz#7dfa3a63c45aecaa17824e656bcdb01f9737154a" - integrity sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz" + integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== dependencies: decode-named-character-reference "^1.0.0" micromark-util-character "^2.0.0" @@ -2595,42 +2916,42 @@ micromark-util-decode-string@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz#0921ac7953dc3f1fd281e3d1932decfdb9382ab1" - integrity sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== micromark-util-html-tag-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz#ae34b01cbe063363847670284c6255bb12138ec4" - integrity sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz" + integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== micromark-util-normalize-identifier@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz#91f9a4e65fe66cc80c53b35b0254ad67aa431d8b" - integrity sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz" + integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== dependencies: micromark-util-symbol "^2.0.0" micromark-util-resolve-all@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz#189656e7e1a53d0c86a38a652b284a252389f364" - integrity sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz" + integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== dependencies: micromark-util-types "^2.0.0" micromark-util-sanitize-uri@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz#ec8fbf0258e9e6d8f13d9e4770f9be64342673de" - integrity sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== dependencies: micromark-util-character "^2.0.0" micromark-util-encode "^2.0.0" micromark-util-symbol "^2.0.0" micromark-util-subtokenize@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz#76129c49ac65da6e479c09d0ec4b5f29ec6eace5" - integrity sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q== + version "2.1.0" + resolved "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz" + integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== dependencies: devlop "^1.0.0" micromark-util-chunked "^2.0.0" @@ -2638,19 +2959,19 @@ micromark-util-subtokenize@^2.0.0: micromark-util-types "^2.0.0" micromark-util-symbol@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044" - integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== micromark-util-types@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e" - integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== + version "2.0.2" + resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== micromark@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.0.tgz#84746a249ebd904d9658cfabc1e8e5f32cbc6249" - integrity sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ== + version "4.0.2" + resolved "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz" + integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== dependencies: "@types/debug" "^4.0.0" debug "^4.0.0" @@ -2682,7 +3003,7 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" -minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -2719,6 +3040,11 @@ object-inspect@^1.13.1: resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" @@ -2734,18 +3060,30 @@ object.assign@^4.1.4, object.assign@^4.1.5: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.7: +object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" + object-keys "^1.1.1" + +object.entries@^1.1.8: version "1.1.8" - resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== dependencies: call-bind "^1.0.7" define-properties "^1.2.1" es-object-atoms "^1.0.0" -object.fromentries@^2.0.7: +object.fromentries@^2.0.8: version "2.0.8" - resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== dependencies: call-bind "^1.0.7" @@ -2753,16 +3091,7 @@ object.fromentries@^2.0.7: es-abstract "^1.23.2" es-object-atoms "^1.0.0" -object.hasown@^1.1.3: - version "1.1.4" - resolved "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz" - integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== - dependencies: - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - -object.values@^1.1.6, object.values@^1.1.7: +object.values@^1.1.6: version "1.2.0" resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz" integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== @@ -2771,12 +3100,15 @@ object.values@^1.1.6, object.values@^1.1.7: define-properties "^1.2.1" es-object-atoms "^1.0.0" -once@^1.3.0: - version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== +object.values@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== dependencies: - wrappy "1" + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" optionator@^0.9.3: version "0.9.3" @@ -2790,6 +3122,15 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" +own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== + dependencies: + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" @@ -2824,12 +3165,11 @@ parse-entities@^2.0.0: is-hexadecimal "^1.0.0" parse-entities@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.1.tgz#4e2a01111fb1c986549b944af39eeda258fc9e4e" - integrity sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w== + version "4.0.2" + resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz" + integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== dependencies: "@types/unist" "^2.0.0" - character-entities "^2.0.0" character-entities-legacy "^3.0.0" character-reference-invalid "^2.0.0" decode-named-character-reference "^1.0.0" @@ -2838,22 +3178,17 @@ parse-entities@^4.0.0: is-hexadecimal "^2.0.0" parse5@^7.0.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" - integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + version "7.2.1" + resolved "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz" + integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== dependencies: - entities "^4.4.0" + entities "^4.5.0" path-exists@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - path-key@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" @@ -2916,9 +3251,14 @@ property-information@^5.0.0: property-information@^6.0.0: version "6.5.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.5.0.tgz#6212fbb52ba757e92ef4fb9d657563b933b7ffec" + resolved "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz" integrity sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig== +property-information@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz" + integrity sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg== + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" @@ -2929,14 +3269,9 @@ punycode@^2.1.0: resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - rc-motion@^2.0.0, rc-motion@^2.0.1: version "2.9.5" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c" + resolved "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz" integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== dependencies: "@babel/runtime" "^7.11.1" @@ -2945,7 +3280,7 @@ rc-motion@^2.0.0, rc-motion@^2.0.1: rc-overflow@^1.3.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.4.1.tgz#e1bcf0375979c24cffa2d87bf83a19ded5fcdf45" + resolved "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.4.1.tgz" integrity sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw== dependencies: "@babel/runtime" "^7.11.1" @@ -2955,7 +3290,7 @@ rc-overflow@^1.3.1: rc-resize-observer@^1.0.0, rc-resize-observer@^1.3.1: version "1.4.3" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569" + resolved "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz" integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ== dependencies: "@babel/runtime" "^7.20.7" @@ -2965,7 +3300,7 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.3.1: rc-select@^14.16.6: version "14.16.6" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.16.6.tgz#1c57a9aa97248b3fd9a830d9bf5df6e9d2ad2c69" + resolved "https://registry.npmjs.org/rc-select/-/rc-select-14.16.6.tgz" integrity sha512-YPMtRPqfZWOm2XGTbx5/YVr1HT0vn//8QS77At0Gjb3Lv+Lbut0IORJPKLWu1hQ3u4GsA0SrDzs7nI8JG7Zmyg== dependencies: "@babel/runtime" "^7.10.1" @@ -2977,17 +3312,17 @@ rc-select@^14.16.6: rc-virtual-list "^3.5.2" rc-util@^5.16.1, rc-util@^5.24.4, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.44.0, rc-util@^5.44.1: - version "5.44.3" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.3.tgz#9eca5039906446113c4032859f88c15234547961" - integrity sha512-q6KCcOFk3rv/zD3MckhJteZxb0VjAIFuf622B7ElK4vfrZdAzs16XR5p3VTdy3+U5jfJU5ACz4QnhLSuAGe5dA== + version "5.44.4" + resolved "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz" + integrity sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w== dependencies: "@babel/runtime" "^7.18.3" react-is "^18.2.0" rc-virtual-list@^3.5.2: - version "3.18.1" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.18.1.tgz#a081237b7e468eaec7e64a896a4261c3ed6f1c4b" - integrity sha512-ARSsD/dey/I4yNQHFYYUaKLUkD1wnD4lRZIvb3rCLMbTMmoFQJRVrWuSfbNt5P5MzMNooEBDvqrUPM4QN7BMNA== + version "3.18.3" + resolved "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.18.3.tgz" + integrity sha512-s1/bZQY2uwnmgXYeXxJkk2cSTz1cdUPDCrxAq/y1WQM115HilFFIvLi+JVFfkD4xCq3TZxGM17FQH4NLesWfwg== dependencies: "@babel/runtime" "^7.20.0" classnames "^2.2.6" @@ -3025,15 +3360,16 @@ react-is@^16.13.1: react-is@^18.2.0: version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== react-markdown@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.0.1.tgz#c05ddbff67fd3b3f839f8c648e6fb35d022397d1" - integrity sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg== + version "9.1.0" + resolved "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz" + integrity sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw== dependencies: "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" devlop "^1.0.0" hast-util-to-jsx-runtime "^2.0.0" html-url-attributes "^3.0.0" @@ -3082,18 +3418,19 @@ react@^18.2.0: dependencies: loose-envify "^1.1.0" -reflect.getprototypeof@^1.0.4: - version "1.0.6" - resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz" - integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg== +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" define-properties "^1.2.1" - es-abstract "^1.23.1" + es-abstract "^1.23.9" es-errors "^1.3.0" - get-intrinsic "^1.2.4" - globalthis "^1.0.3" - which-builtin-type "^1.1.3" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" refractor@^3.6.0: version "3.6.0" @@ -3119,9 +3456,21 @@ regexp.prototype.flags@^1.5.2: es-errors "^1.3.0" set-function-name "^2.0.1" +regexp.prototype.flags@^1.5.3: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" + set-function-name "^2.0.2" + rehype-raw@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/rehype-raw/-/rehype-raw-7.0.0.tgz#59d7348fd5dbef3807bbaa1d443efd2dd85ecee4" + resolved "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz" integrity sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww== dependencies: "@types/hast" "^3.0.0" @@ -3130,7 +3479,7 @@ rehype-raw@^7.0.0: remark-gemoji@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/remark-gemoji/-/remark-gemoji-8.0.0.tgz#525380bdf603f94856ee2b2cbd7e6947b07ca72a" + resolved "https://registry.npmjs.org/remark-gemoji/-/remark-gemoji-8.0.0.tgz" integrity sha512-/fL9rc72FYwFGtOKcT+QeQdx9Q9t5v4N6KLXSDOTEgaedzK85I9judBqB2eqz+g4b0ERMejlwSOuPK+wket6aA== dependencies: "@types/mdast" "^4.0.0" @@ -3138,9 +3487,9 @@ remark-gemoji@^8.0.0: mdast-util-find-and-replace "^3.0.0" remark-gfm@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-4.0.0.tgz#aea777f0744701aa288b67d28c43565c7e8c35de" - integrity sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA== + version "4.0.1" + resolved "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz" + integrity sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg== dependencies: "@types/mdast" "^4.0.0" mdast-util-gfm "^3.0.0" @@ -3151,7 +3500,7 @@ remark-gfm@^4.0.0: remark-parse@^11.0.0: version "11.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" + resolved "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz" integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== dependencies: "@types/mdast" "^4.0.0" @@ -3160,9 +3509,9 @@ remark-parse@^11.0.0: unified "^11.0.0" remark-rehype@^11.0.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.0.tgz#d5f264f42bcbd4d300f030975609d01a1697ccdc" - integrity sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g== + version "11.1.1" + resolved "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz" + integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" @@ -3172,7 +3521,7 @@ remark-rehype@^11.0.0: remark-stringify@^11.0.0: version "11.0.0" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" + resolved "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz" integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== dependencies: "@types/mdast" "^4.0.0" @@ -3181,7 +3530,7 @@ remark-stringify@^11.0.0: resize-observer-polyfill@^1.5.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== resolve-from@^4.0.0: @@ -3198,18 +3547,6 @@ resolve@^2.0.0-next.5: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rollup@^4.13.0: version "4.13.2" resolved "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz" @@ -3234,13 +3571,6 @@ rollup@^4.13.0: "@rollup/rollup-win32-x64-msvc" "4.13.2" fsevents "~2.3.2" -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - safe-array-concat@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz" @@ -3251,6 +3581,25 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" +safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" + isarray "^2.0.5" + +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" + safe-regex-test@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz" @@ -3260,6 +3609,15 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + scheduler@^0.23.0: version "0.23.0" resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz" @@ -3272,7 +3630,7 @@ semver@^6.3.1: resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -set-function-length@^1.2.1: +set-function-length@^1.2.1, set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== @@ -3294,6 +3652,15 @@ set-function-name@^2.0.1, set-function-name@^2.0.2: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" @@ -3306,7 +3673,36 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -side-channel@^1.0.4, side-channel@^1.0.6: +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz" integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== @@ -3316,6 +3712,17 @@ side-channel@^1.0.4, side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + source-map-js@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz" @@ -3328,26 +3735,48 @@ space-separated-tokens@^1.0.0: space-separated-tokens@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz" integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== -string.prototype.matchall@^4.0.10: - version "4.0.11" - resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz" - integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== +string.prototype.matchall@^4.0.12: + version "4.0.12" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz#6c88740e49ad4956b1332a911e949583a275d4c0" + integrity sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" define-properties "^1.2.1" - es-abstract "^1.23.2" + es-abstract "^1.23.6" es-errors "^1.3.0" es-object-atoms "^1.0.0" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.7" - regexp.prototype.flags "^1.5.2" + get-intrinsic "^1.2.6" + gopd "^1.2.0" + has-symbols "^1.1.0" + internal-slot "^1.1.0" + regexp.prototype.flags "^1.5.3" set-function-name "^2.0.2" - side-channel "^1.0.6" + side-channel "^1.1.0" + +string.prototype.repeat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" string.prototype.trim@^1.2.9: version "1.2.9" @@ -3368,7 +3797,17 @@ string.prototype.trimend@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string.prototype.trimstart@^1.0.7: +string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.7, string.prototype.trimstart@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz" integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== @@ -3379,30 +3818,23 @@ string.prototype.trimstart@^1.0.7: stringify-entities@^4.0.0: version "4.0.4" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" + resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz" integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== dependencies: character-entities-html4 "^2.0.0" character-entities-legacy "^3.0.0" -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== style-to-object@^1.0.0: - version "1.0.6" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.6.tgz#0c28aed8be1813d166c60d962719b2907c26547b" - integrity sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA== + version "1.0.8" + resolved "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz" + integrity sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== dependencies: - inline-style-parser "0.2.3" + inline-style-parser "0.2.4" supports-color@^5.3.0: version "5.5.0" @@ -3423,11 +3855,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" @@ -3435,12 +3862,12 @@ to-fast-properties@^2.0.0: trim-lines@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + resolved "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz" integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== trough@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" + resolved "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz" integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== type-check@^0.4.0, type-check@~0.4.0: @@ -3450,11 +3877,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz" @@ -3464,6 +3886,15 @@ typed-array-buffer@^1.0.2: es-errors "^1.3.0" is-typed-array "^1.1.13" +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" + typed-array-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz" @@ -3475,6 +3906,17 @@ typed-array-byte-length@^1.0.1: has-proto "^1.0.3" is-typed-array "^1.1.13" +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== + dependencies: + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" + typed-array-byte-offset@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz" @@ -3487,6 +3929,19 @@ typed-array-byte-offset@^1.0.2: has-proto "^1.0.3" is-typed-array "^1.1.13" +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" + typed-array-length@^1.0.5: version "1.0.6" resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz" @@ -3499,6 +3954,18 @@ typed-array-length@^1.0.5: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" @@ -3509,10 +3976,20 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== + dependencies: + call-bound "^1.0.3" + has-bigints "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" + unified@^11.0.0: - version "11.0.4" - resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.4.tgz#f4be0ac0fe4c88cb873687c07c64c49ed5969015" - integrity sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ== + version "11.0.5" + resolved "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz" + integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== dependencies: "@types/unist" "^3.0.0" bail "^2.0.0" @@ -3524,36 +4001,28 @@ unified@^11.0.0: unist-util-is@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" + resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz" integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== dependencies: "@types/unist" "^3.0.0" unist-util-position@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + resolved "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz" integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== dependencies: "@types/unist" "^3.0.0" -unist-util-remove-position@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz#fea68a25658409c9460408bc6b4991b965b52163" - integrity sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q== - dependencies: - "@types/unist" "^3.0.0" - unist-util-visit "^5.0.0" - unist-util-stringify-position@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz" integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== dependencies: "@types/unist" "^3.0.0" unist-util-visit-parents@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" + resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz" integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== dependencies: "@types/unist" "^3.0.0" @@ -3561,7 +4030,7 @@ unist-util-visit-parents@^6.0.0: unist-util-visit@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" + resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz" integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== dependencies: "@types/unist" "^3.0.0" @@ -3584,35 +4053,34 @@ uri-js@^4.2.2: punycode "^2.1.0" usehooks-ts@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/usehooks-ts/-/usehooks-ts-3.1.0.tgz#156119f36efc85f1b1952616c02580f140950eca" - integrity sha512-bBIa7yUyPhE1BCc0GmR96VU/15l/9gP1Ch5mYdLcFBaFGQsdmXkvjV0TtOqW1yUd6VjIwDunm+flSciCQXujiw== + version "3.1.1" + resolved "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz" + integrity sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA== dependencies: lodash.debounce "^4.0.8" vfile-location@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.2.tgz#220d9ca1ab6f8b2504a4db398f7ebc149f9cb464" - integrity sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg== + version "5.0.3" + resolved "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz" + integrity sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg== dependencies: "@types/unist" "^3.0.0" vfile "^6.0.0" vfile-message@^4.0.0: version "4.0.2" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" + resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz" integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== dependencies: "@types/unist" "^3.0.0" unist-util-stringify-position "^4.0.0" vfile@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.1.tgz#1e8327f41eac91947d4fe9d237a2dd9209762536" - integrity sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw== + version "6.0.3" + resolved "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== dependencies: "@types/unist" "^3.0.0" - unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" vite-plugin-chunk-split@^0.5.0: @@ -3637,7 +4105,7 @@ vite@^5.2.0: web-namespaces@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" + resolved "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz" integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== which-boxed-primitive@^1.0.2: @@ -3651,27 +4119,39 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-builtin-type@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz" - integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== dependencies: - function.prototype.name "^1.1.5" - has-tostringtag "^1.0.0" + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" + +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== + dependencies: + call-bound "^1.0.2" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" is-async-function "^2.0.0" - is-date-object "^1.0.5" - is-finalizationregistry "^1.0.2" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" is-generator-function "^1.0.10" - is-regex "^1.1.4" + is-regex "^1.2.1" is-weakref "^1.0.2" isarray "^2.0.5" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" + which-boxed-primitive "^1.1.0" + which-collection "^1.0.2" + which-typed-array "^1.1.16" -which-collection@^1.0.1: +which-collection@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== dependencies: is-map "^2.0.3" @@ -3679,7 +4159,7 @@ which-collection@^1.0.1: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: +which-typed-array@^1.1.14, which-typed-array@^1.1.15: version "1.1.15" resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz" integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== @@ -3690,6 +4170,18 @@ which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.2" +which-typed-array@^1.1.16, which-typed-array@^1.1.18: + version "1.1.18" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.18.tgz#df2389ebf3fbb246a71390e90730a9edb6ce17ad" + integrity sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" + for-each "^0.3.3" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + which@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" @@ -3697,11 +4189,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wrappy@1: - version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - xtend@^4.0.0: version "4.0.2" resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" @@ -3719,5 +4206,5 @@ yocto-queue@^0.1.0: zwitch@^2.0.0: version "2.0.4" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz" integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== From 2eb09fd31555e0e2f14fac6256faa68a5dc7ced1 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 1 Mar 2025 11:20:45 +0300 Subject: [PATCH 041/117] FRONTEND_V2: lint project and fix build --- FRONTEND_V2/Dockerfile | 3 ++- FRONTEND_V2/package.json | 1 + FRONTEND_V2/src/components/BreadcrumbsElement.jsx | 1 - FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx | 1 - FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx | 1 - FRONTEND_V2/src/pages/admin/AdminSeeSendPage.jsx | 2 -- FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx | 6 +++--- FRONTEND_V2/src/pages/user/ChampsPage.jsx | 1 - FRONTEND_V2/src/pages/user/StatsPage.jsx | 1 - 9 files changed, 6 insertions(+), 11 deletions(-) diff --git a/FRONTEND_V2/Dockerfile b/FRONTEND_V2/Dockerfile index 6ee9f3c..8214f8b 100644 --- a/FRONTEND_V2/Dockerfile +++ b/FRONTEND_V2/Dockerfile @@ -7,7 +7,8 @@ COPY package.json /app RUN yarn install COPY . /app -RUN yarn lint +# Disabled lint while developing +# RUN yarn lint RUN yarn build # production environment diff --git a/FRONTEND_V2/package.json b/FRONTEND_V2/package.json index 30c4c87..9e99064 100644 --- a/FRONTEND_V2/package.json +++ b/FRONTEND_V2/package.json @@ -7,6 +7,7 @@ "dev": "vite", "build": "vite build", "lint": "eslint --config eslint.config.js src/", + "lint:html": "eslint --config eslint.config.js src/ --format html > eslint-report.html", "preview": "vite preview" }, "dependencies": { diff --git a/FRONTEND_V2/src/components/BreadcrumbsElement.jsx b/FRONTEND_V2/src/components/BreadcrumbsElement.jsx index 0c4be91..1bfd2b2 100644 --- a/FRONTEND_V2/src/components/BreadcrumbsElement.jsx +++ b/FRONTEND_V2/src/components/BreadcrumbsElement.jsx @@ -1,4 +1,3 @@ -import Card from "./bootstrap/Card.jsx"; import {Link} from "react-router-dom"; const BreadcrumbsElement = ({name, url, active}) => { diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx index ac5d2d3..683bc2d 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx @@ -1,5 +1,4 @@ import Card from "../../components/bootstrap/Card.jsx"; -import ResponsiveTable from "../../components/bootstrap/ResponsiveTable.jsx"; import useCachedGetAPI from "../../hooks/useGetAPI.js"; import {useEffect} from "react"; import {Link} from "react-router-dom"; diff --git a/FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx b/FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx index 0f29cd8..d861fba 100644 --- a/FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx @@ -1,5 +1,4 @@ import Card from "../../components/bootstrap/Card.jsx"; -import ResponsiveTable from "../../components/bootstrap/ResponsiveTable.jsx"; import useCachedGetAPI from "../../hooks/useGetAPI.js"; import {useEffect} from "react"; import {Link} from "react-router-dom"; diff --git a/FRONTEND_V2/src/pages/admin/AdminSeeSendPage.jsx b/FRONTEND_V2/src/pages/admin/AdminSeeSendPage.jsx index 9345c21..54be317 100644 --- a/FRONTEND_V2/src/pages/admin/AdminSeeSendPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminSeeSendPage.jsx @@ -5,8 +5,6 @@ import useCachedGetAPI from "../../hooks/useGetAPI.js"; import LazySyntaxHighlight from "../../components/lazy/LazySyntaxHightlight.jsx"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; import ResponsiveTable from "../../components/bootstrap/ResponsiveTable.jsx"; -import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; -import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; export const AdminSeeSendPage = () => { diff --git a/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx b/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx index 45bda2e..8907394 100644 --- a/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx @@ -1,7 +1,7 @@ import Card from "../../components/bootstrap/Card.jsx"; import useCachedGetAPI from "../../hooks/useGetAPI.js"; import {useEffect, useState} from "react"; -import {Link, useNavigate, useParams} from "react-router-dom"; +import {useNavigate, useParams} from "react-router-dom"; import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; @@ -104,8 +104,8 @@ export const AdminUsersDetailPage = () => { ) }) } - {/**/} - {/**/} + {/**/} + {/**/}
diff --git a/FRONTEND_V2/src/pages/user/ChampsPage.jsx b/FRONTEND_V2/src/pages/user/ChampsPage.jsx index be60262..c483bbe 100644 --- a/FRONTEND_V2/src/pages/user/ChampsPage.jsx +++ b/FRONTEND_V2/src/pages/user/ChampsPage.jsx @@ -1,5 +1,4 @@ import Card from "../../components/bootstrap/Card.jsx"; -import ResponsiveTable from "../../components/bootstrap/ResponsiveTable.jsx"; import useCachedGetAPI from "../../hooks/useGetAPI.js"; import {useEffect} from "react"; import {Link} from "react-router-dom"; diff --git a/FRONTEND_V2/src/pages/user/StatsPage.jsx b/FRONTEND_V2/src/pages/user/StatsPage.jsx index ad50286..68be12b 100644 --- a/FRONTEND_V2/src/pages/user/StatsPage.jsx +++ b/FRONTEND_V2/src/pages/user/StatsPage.jsx @@ -5,7 +5,6 @@ import useCachedGetAPI from "../../hooks/useGetAPI.js"; import {getCookie} from "../../utils/cookies.js"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; import ResponsiveTable from "../../components/bootstrap/ResponsiveTable.jsx"; -import log from "eslint-plugin-react/lib/util/log.js"; import {formatDate} from "../../utils/format.js"; const StatsPage = () => { From 42a28fcc5a1b7e87e2951d9e1063a79ae51fd5ee Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sun, 2 Mar 2025 22:52:49 +0300 Subject: [PATCH 042/117] docker ready --- FRONTEND_V2/src/utils/settings.js | 15 +++++--- GATEWAY/nginx.conf | 22 ------------ docker-compose.yml | 59 +++++-------------------------- 3 files changed, 20 insertions(+), 76 deletions(-) diff --git a/FRONTEND_V2/src/utils/settings.js b/FRONTEND_V2/src/utils/settings.js index 5b57664..99b8bb1 100644 --- a/FRONTEND_V2/src/utils/settings.js +++ b/FRONTEND_V2/src/utils/settings.js @@ -1,6 +1,13 @@ import axios from "axios"; -const productionBuild = import.meta.env.VITE_ENV === "production"; -if (!productionBuild) { - axios.defaults.baseURL = `${document.location.protocol}//${document.location.hostname}:${8080}` -} +// const productionBuild = import.meta.env.VITE_ENV === "production"; +// if (!productionBuild) { +// axios.defaults.baseURL = `${document.location.protocol}//${document.location.hostname}:${8080}` +// } +// const baseUrl = import.meta.env.VITE_API_ENDPOINT +// +// console.log("BASEURL: "+ baseUrl) +// +// if (baseUrl && baseUrl !== ""){ +// axios.defaults.baseURL = baseUrl +// } diff --git a/GATEWAY/nginx.conf b/GATEWAY/nginx.conf index f87fe1e..0ce73f6 100644 --- a/GATEWAY/nginx.conf +++ b/GATEWAY/nginx.conf @@ -19,28 +19,6 @@ server { location /api { proxy_pass http://backend:8000; } - - location /admin { - proxy_pass http://backend:8000; - } - - location /teacher { - proxy_pass http://frontend:80/; - } - - location /assets { - proxy_pass http://frontend:80/assets; - } - - location /cdn { - proxy_pass http://frontend:80/cdn; - } - - location /icons { - proxy_pass http://frontend:80/icons; - } - - location /frv2 { proxy_pass http://frontend-v2:80/frv2; diff --git a/docker-compose.yml b/docker-compose.yml index 7b82882..627441a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,39 +1,26 @@ version: "3.9" services: - frontend: - build: FRONTEND/. - image: ghcr.io/codebattles-nn/frontend - ports: - - "8001:80" - restart: unless-stopped - networks: - - with-internet - - no-internet - frontend-v2: build: FRONTEND_V2/. image: ghcr.io/codebattles-nn/frontend-v2 ports: - "8002:80" restart: unless-stopped + environment: + VITE_ENV: "production" + networks: - with-internet - no-internet backend: - build: BACKEND/. - image: ghcr.io/codebattles-nn/backend + build: BACKEND_V2/. + image: ghcr.io/codebattles-nn/backend-v2 environment: - CHECKER_PORT: 7070 - DB_HOST: postgres - DB_USERNAME: "postgres" - DB_PASSWORD: "admin" - DB_NAME: "cb" - HASH_SALT: 4a5ecb2c236d1c4c3e670fcc76b8d1566c94d096bcf5f700511bb039507ad518 - REDIS_HOST: redis - ADMIN_LOGIN: adminnn23 - ADMIN_PASSWORD: goodprog - PYTHONUNBUFFERED: TRUE + SERVER_PORT: 8000 + SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/cb + SPRING_DATASOURCE_USERNAME: postgres + SPRING_DATASOURCE_PASSWORD: admin ports: - "8000:8000" depends_on: @@ -45,17 +32,6 @@ services: networks: - with-internet - no-internet - - - redis: - image: redis:alpine3.18 - ports: - - 6379:6379 - restart: unless-stopped - networks: - - with-internet - - no-internet - postgres: image: postgres:13.3-alpine environment: @@ -82,24 +58,7 @@ services: ports: - "2500:80" depends_on: - - frontend - - backend - restart: unless-stopped - networks: - - with-internet - - no-internet - - init_configurer: - build: INIT_CONFIGURER/. - image: ghcr.io/codebattles-nn/init-configurer - environment: - DB_HOST: postgres - DB_USERNAME: "postgres" - DB_PASSWORD: "admin" - DB_NAME: "cb" - links: - backend - - postgres restart: unless-stopped networks: - with-internet From b7cd1bab2a1c11cd34d3ab25bf1156a04b1439b0 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sun, 2 Mar 2025 23:11:43 +0300 Subject: [PATCH 043/117] backend_v2: change cors settings --- .../backend/core/config/SecurityConfig.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt index abec3c9..97e55c6 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt @@ -54,8 +54,16 @@ class SecurityConfig( @Bean fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { http.csrf().disable() - .cors() - .and() + .cors { cors -> + cors.configurationSource { request -> + CorsConfiguration().applyPermitDefaultValues().also { + it.allowedOriginPatterns = listOf("*") + it.allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS") + it.allowedHeaders = listOf("*") + it.allowCredentials = true + } + } + } .authorizeRequests() .requestMatchers("/api/auth/**").permitAll() .requestMatchers( From 361a2f07e8d055e935f38c40cd8dbca361648f98 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:49:14 +0300 Subject: [PATCH 044/117] backend_v2: fix some --- BACKEND_V2/Dockerfile | 10 ++++++++++ BACKEND_V2/build.gradle.kts | 5 ++++- .../dto/mapper/CompetitionsCreateMapper.kt | 19 ------------------- .../codebattles/backend/entity/Competition.kt | 2 +- .../ru/codebattles/backend/entity/User.kt | 2 ++ .../backend/services/CompetitionService.kt | 9 +-------- .../web/controllers/UsersController.kt | 9 +++++++-- 7 files changed, 25 insertions(+), 31 deletions(-) create mode 100644 BACKEND_V2/Dockerfile delete mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsCreateMapper.kt diff --git a/BACKEND_V2/Dockerfile b/BACKEND_V2/Dockerfile new file mode 100644 index 0000000..5231258 --- /dev/null +++ b/BACKEND_V2/Dockerfile @@ -0,0 +1,10 @@ +FROM openjdk:17-jdk-slim AS build +WORKDIR /app +COPY . . +RUN ./gradlew build -x test + +FROM openjdk:17-jdk-slim +WORKDIR /app +COPY --from=build /app/build/libs/*.jar app.jar +EXPOSE 8080 +CMD ["java", "-jar", "app.jar"] diff --git a/BACKEND_V2/build.gradle.kts b/BACKEND_V2/build.gradle.kts index 0f20e83..e8486fe 100644 --- a/BACKEND_V2/build.gradle.kts +++ b/BACKEND_V2/build.gradle.kts @@ -4,7 +4,7 @@ plugins { id("org.springframework.boot") version "3.4.1" id("io.spring.dependency-management") version "1.1.7" kotlin("plugin.jpa") version "1.9.25" - id("org.jetbrains.kotlin.kapt") version "1.9.0" + id("org.jetbrains.kotlin.kapt") version "1.9.25" } group = "ru.codebattles" @@ -63,6 +63,9 @@ tasks.withType { kapt { correctErrorTypes = true + arguments { + arg("mapstruct.unmappedTargetPolicy", "ignore") + } } sourceSets { diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsCreateMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsCreateMapper.kt deleted file mode 100644 index 225e619..0000000 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsCreateMapper.kt +++ /dev/null @@ -1,19 +0,0 @@ -package ru.codebattles.backend.dto.mapper - -import org.mapstruct.Mapper -import org.mapstruct.Mapping -import org.mapstruct.NullValuePropertyMappingStrategy -import ru.codebattles.backend.dto.CompetitionCreateDto -import ru.codebattles.backend.dto.mapper.core.AbstractMapper -import ru.codebattles.backend.entity.Competition -import ru.codebattles.backend.entity.User - -@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) -interface CompetitionsCreateMapper : AbstractMapper { - @Deprecated("Use method with other signature") - override fun fromDto(obj: CompetitionCreateDto): Competition - - @Mapping(target = "organizer", source = "user") - fun fromDto(dto: CompetitionCreateDto, user: User): Competition - -} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt index d3f9093..f502427 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt @@ -13,7 +13,7 @@ data class Competition( val checkers: MutableSet? = mutableSetOf(), @ManyToOne - val organizer: User, + val organizer: User?, @Column(nullable = false) val name: String, diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt index 99dc56f..fa05cc2 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt @@ -14,6 +14,8 @@ data class User( @Column(name = "password") var mpassword: String?, + var name: String? = "", + @ManyToMany(fetch = FetchType.EAGER) var roles: MutableSet = mutableSetOf(), diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt index 44075c4..adbd4dd 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt @@ -7,7 +7,6 @@ import ru.codebattles.backend.dto.CompetitionCreateDto import ru.codebattles.backend.dto.CompetitionDto import ru.codebattles.backend.dto.CompetitionsProblemsDto import ru.codebattles.backend.dto.UserDto -import ru.codebattles.backend.dto.mapper.CompetitionsCreateMapper import ru.codebattles.backend.dto.mapper.CompetitionsMapper import ru.codebattles.backend.dto.mapper.CompetitionsProblemsMapper import ru.codebattles.backend.dto.mapper.UserMapper @@ -28,7 +27,6 @@ class CompetitionService( private val competitionProblemsRepository: CompetitionProblemsRepository, private val competitionsProblemsMapper: CompetitionsProblemsMapper, private val competitionsMapper: CompetitionsMapper, - private val competitionsCreateMapper: CompetitionsCreateMapper, private val testRepo: TestRepo, ) { @@ -96,11 +94,6 @@ class CompetitionService( } fun create(competitionDto: CompetitionCreateDto, user: User): CompetitionDto { - val convertedFromDto = competitionsCreateMapper.fromDto(competitionDto, user) - val competition = competitionRepository.save(convertedFromDto) - - println() - - return competitionsMapper.toDto(competition) + throw IllegalStateException("Not implemented") } } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt index c2d0397..893b1a3 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt @@ -5,6 +5,8 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +import ru.codebattles.backend.dto.UserDto +import ru.codebattles.backend.dto.mapper.UserMapper import ru.codebattles.backend.entity.User import ru.codebattles.backend.repository.UserRepository import java.util.* @@ -14,6 +16,7 @@ import java.util.* @SecurityRequirement(name = "JWT") class UsersController( val userRepository: UserRepository, + val userMapper: UserMapper, ) { @GetMapping("me") fun getProfile(@AuthenticationPrincipal user: User): Optional { @@ -22,7 +25,9 @@ class UsersController( @GetMapping - fun getAll(@AuthenticationPrincipal user: User): MutableList { - return userRepository.findAll() + fun getAll(@AuthenticationPrincipal user: User): List { + return userMapper.toDtoS( + userRepository.findAll() + ) } } From 44d3e29bbcd62c5d68132e76cf4262b8e02c1fd2 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:49:50 +0300 Subject: [PATCH 045/117] frontend: fix visual bug --- FRONTEND_V2/src/components/AdminHeader.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FRONTEND_V2/src/components/AdminHeader.jsx b/FRONTEND_V2/src/components/AdminHeader.jsx index 7e95d30..bf4565e 100644 --- a/FRONTEND_V2/src/components/AdminHeader.jsx +++ b/FRONTEND_V2/src/components/AdminHeader.jsx @@ -5,7 +5,7 @@ export const AdminHeader = () => { return ( соревнования - задачи + задачи чекеры интерфейс ученика From 3d5ca2bfdc8b538c56d08a33713738c20156a193 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:17:52 +0300 Subject: [PATCH 046/117] backend_2: api for create user --- .../core/config/PasswordEncoderConfig.kt | 15 +++++++++++++++ .../backend/core/config/SecurityConfig.kt | 8 ++------ .../ru/codebattles/backend/dto/CreateUserDto.kt | 15 +++++++++++++++ .../codebattles/backend/services/UserService.kt | 17 ++++++++++++++++- .../backend/web/controllers/UsersController.kt | 17 ++++++++++++++--- 5 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/PasswordEncoderConfig.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CreateUserDto.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/PasswordEncoderConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/PasswordEncoderConfig.kt new file mode 100644 index 0000000..0ba7e57 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/PasswordEncoderConfig.kt @@ -0,0 +1,15 @@ +package ru.codebattles.backend.core.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder + +@Configuration +class PasswordEncoderConfig { + @Bean + fun encoder(): PasswordEncoder { + return BCryptPasswordEncoder() + } + +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt index 97e55c6..8c65388 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt @@ -17,7 +17,8 @@ import ru.codebattles.backend.core.filter.JwtAuthenticationFilter @Configuration class SecurityConfig( - private val jwtAuthenticationFilter: JwtAuthenticationFilter + private val jwtAuthenticationFilter: JwtAuthenticationFilter, + private val passwordEncoder: PasswordEncoder, ) { @Bean @@ -82,11 +83,6 @@ class SecurityConfig( return http.build() } - @Bean - fun encoder(): PasswordEncoder { - return BCryptPasswordEncoder() - } - @Bean fun corsConfigurationSource(): CorsConfigurationSource { val configuration = CorsConfiguration() diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CreateUserDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CreateUserDto.kt new file mode 100644 index 0000000..331d44c --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/CreateUserDto.kt @@ -0,0 +1,15 @@ +package ru.codebattles.backend.dto + +import jakarta.validation.constraints.NotEmpty +import jakarta.validation.constraints.NotNull + +/** + * DTO for {@link ru.codebattles.backend.entity.User} + */ +data class CreateUserDto( + @field:NotNull @field:NotEmpty + val musername: String? = null, + @field:NotNull @field:NotEmpty + val mpassword: String? = null, + val name: String? = "" +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserService.kt index aaae56b..3571dfb 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/UserService.kt @@ -1,15 +1,30 @@ package ru.codebattles.backend.services +import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.stereotype.Service +import ru.codebattles.backend.dto.CreateUserDto import ru.codebattles.backend.entity.User import ru.codebattles.backend.repository.UserRepository @Service class UserService( - private val userRepository: UserRepository + private val userRepository: UserRepository, + private val passwordEncoder: PasswordEncoder, ) { fun getByUsername(username: String): User { return userRepository.findByMusername(username) } + fun create(userDto: CreateUserDto): User { + + val user = User( + mpassword = passwordEncoder.encode(userDto.mpassword), + musername = userDto.musername, + name = userDto.name + ) + + userRepository.save(user) + + return user + } } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt index 893b1a3..92d0846 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt @@ -2,13 +2,13 @@ package ru.codebattles.backend.web.controllers import io.swagger.v3.oas.annotations.security.SecurityRequirement import org.springframework.security.core.annotation.AuthenticationPrincipal -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.* +import ru.codebattles.backend.dto.CreateUserDto import ru.codebattles.backend.dto.UserDto import ru.codebattles.backend.dto.mapper.UserMapper import ru.codebattles.backend.entity.User import ru.codebattles.backend.repository.UserRepository +import ru.codebattles.backend.services.UserService import java.util.* @RestController @@ -17,6 +17,7 @@ import java.util.* class UsersController( val userRepository: UserRepository, val userMapper: UserMapper, + private val userService: UserService, ) { @GetMapping("me") fun getProfile(@AuthenticationPrincipal user: User): Optional { @@ -30,4 +31,14 @@ class UsersController( userRepository.findAll() ) } + + @PostMapping + fun create(@RequestBody(required = true) userDto: CreateUserDto): UserDto { + + return userMapper.toDto( + userService.create(userDto) + ) + } + + } From c2e84acacd1ac6f919eecf42cdf8f743976c1c7f Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:20:35 +0300 Subject: [PATCH 047/117] frontend: fix users fetching --- FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx b/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx index 8907394..e047b25 100644 --- a/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminUsersDetailPage.jsx @@ -27,6 +27,7 @@ export const AdminUsersDetailPage = () => { useEffect(() => { update() + updateUsers() }, []); // console.log(data) From 0789014fb97baa6e513b173c3a6d4f9690307836 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:53:08 +0300 Subject: [PATCH 048/117] backend_v2: add endpoint to link users to competition --- .../kotlin/ru/codebattles/backend/entity/User.kt | 9 +++++++++ .../backend/services/CompetitionService.kt | 6 ++++++ .../backend/web/controllers/UsersController.kt | 14 ++++++++++++++ .../backend/web/entity/LinkUserRequest.kt | 7 +++++++ .../codebattles/backend/web/entity/OkResponse.kt | 6 ++++++ 5 files changed, 42 insertions(+) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/LinkUserRequest.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/OkResponse.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt index fa05cc2..e826d91 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/User.kt @@ -32,5 +32,14 @@ data class User( return authorities.contains(SimpleGrantedAuthority("ROLE_ADMIN")) } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is User) return false + return this.id == other.id + } + + override fun hashCode(): Int { + return id?.hashCode() ?: 0 + } } \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt index adbd4dd..285056a 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt @@ -75,7 +75,13 @@ class CompetitionService( fun patchUsers(compId: Long, usersIds: Set){ val competition = competitionRepository.findById(compId).orElseThrow() competition.members = userRepository.findByIdIn(usersIds) + competitionRepository.save(competition) + } + fun joinUser(compId: Long, userId: Long){ + val competition = competitionRepository.findById(compId).orElseThrow() + val user = userRepository.getById(userId) + competition.members?.add(user) competitionRepository.save(competition) } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt index 92d0846..d837865 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/UsersController.kt @@ -8,7 +8,10 @@ import ru.codebattles.backend.dto.UserDto import ru.codebattles.backend.dto.mapper.UserMapper import ru.codebattles.backend.entity.User import ru.codebattles.backend.repository.UserRepository +import ru.codebattles.backend.services.CompetitionService import ru.codebattles.backend.services.UserService +import ru.codebattles.backend.web.entity.LinkUserRequest +import ru.codebattles.backend.web.entity.OkResponse import java.util.* @RestController @@ -18,6 +21,7 @@ class UsersController( val userRepository: UserRepository, val userMapper: UserMapper, private val userService: UserService, + private val competitionService: CompetitionService, ) { @GetMapping("me") fun getProfile(@AuthenticationPrincipal user: User): Optional { @@ -40,5 +44,15 @@ class UsersController( ) } + @PostMapping("link") + fun linkUser(@RequestBody(required = true) linkReq: LinkUserRequest): OkResponse { + + competitionService.joinUser( + linkReq.competitionId, + linkReq.userId, + ) + + return OkResponse() + } } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/LinkUserRequest.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/LinkUserRequest.kt new file mode 100644 index 0000000..148579d --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/LinkUserRequest.kt @@ -0,0 +1,7 @@ +package ru.codebattles.backend.web.entity + +data class LinkUserRequest( + val userId: Long, + val competitionId: Long, + +) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/OkResponse.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/OkResponse.kt new file mode 100644 index 0000000..5621100 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/OkResponse.kt @@ -0,0 +1,6 @@ +package ru.codebattles.backend.web.entity + +data class OkResponse( + val status: String = "OK", + +) From 2627fd8e475d576d4936dbd7ceb5fe130150e963 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 26 Apr 2025 19:38:33 +0300 Subject: [PATCH 049/117] backend_v2: competition creation --- .../backend/services/CompetitionService.kt | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt index 285056a..dd48bf8 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt @@ -10,6 +10,7 @@ import ru.codebattles.backend.dto.UserDto import ru.codebattles.backend.dto.mapper.CompetitionsMapper import ru.codebattles.backend.dto.mapper.CompetitionsProblemsMapper import ru.codebattles.backend.dto.mapper.UserMapper +import ru.codebattles.backend.entity.Competition import ru.codebattles.backend.entity.LeaderBoardAllTasksQuery import ru.codebattles.backend.entity.Leaderboard import ru.codebattles.backend.entity.User @@ -72,13 +73,13 @@ class CompetitionService( ) } - fun patchUsers(compId: Long, usersIds: Set){ + fun patchUsers(compId: Long, usersIds: Set) { val competition = competitionRepository.findById(compId).orElseThrow() competition.members = userRepository.findByIdIn(usersIds) competitionRepository.save(competition) } - fun joinUser(compId: Long, userId: Long){ + fun joinUser(compId: Long, userId: Long) { val competition = competitionRepository.findById(compId).orElseThrow() val user = userRepository.getById(userId) competition.members?.add(user) @@ -92,7 +93,6 @@ class CompetitionService( } - fun getAllByUser(user: User): List { return competitionsMapper.toDtoS( competitionRepository.getByMembersContaining(user) @@ -100,6 +100,18 @@ class CompetitionService( } fun create(competitionDto: CompetitionCreateDto, user: User): CompetitionDto { - throw IllegalStateException("Not implemented") + val competition = Competition( + organizer = user, + name = competitionDto.name, + description = competitionDto.description, + ) + + competitionRepository.save( + competition + ) + + return competitionsMapper.toDto( + competition + ) } } From d6c4ff5292dbba9b1ff82c8eb105401eb7e081a0 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 26 Apr 2025 19:59:36 +0300 Subject: [PATCH 050/117] backend_v2: fix problem fetch --- .../backend/repository/CompetitionProblemsRepository.kt | 1 + .../ru/codebattles/backend/services/CompetitionService.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt index 453f638..f4d9a90 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/CompetitionProblemsRepository.kt @@ -6,4 +6,5 @@ import ru.codebattles.backend.entity.CompetitionsProblems interface CompetitionProblemsRepository : JpaRepository { fun getAllByCompetitionId(id: Long): List fun getFirstByCompetitionIdAndProblemId(competition_id: Long, problem_id: Long): CompetitionsProblems + fun getFirstByCompetitionIdAndId(competition_id: Long, id: Long): CompetitionsProblems } \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt index dd48bf8..047eece 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt @@ -69,7 +69,7 @@ class CompetitionService( fun getProblemById(id: Long, problemId: Long): CompetitionsProblemsDto { return competitionsProblemsMapper.toDto( - competitionProblemsRepository.getFirstByCompetitionIdAndProblemId(id, problemId) + competitionProblemsRepository.getFirstByCompetitionIdAndId(id, problemId) ) } From d0afb5a2d7b5a175ec6914e4b8af8517a08a903a Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sun, 27 Apr 2025 11:18:59 +0300 Subject: [PATCH 051/117] backend_v2: add CompetitionsProblems api --- .../services/CompetitionsProblemsService.kt | 73 +++++++++++++++++++ .../CompetitionsProblemsController.kt | 73 +++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsProblemsController.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt new file mode 100644 index 0000000..97acaea --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt @@ -0,0 +1,73 @@ +package ru.codebattles.backend.services; + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Service +import org.springframework.web.server.ResponseStatusException +import ru.codebattles.backend.entity.CompetitionsProblems +import ru.codebattles.backend.repository.CompetitionProblemsRepository +import java.io.IOException +import java.util.* + +@Service +class CompetitionsProblemsService( + private val competitionProblemsRepository: CompetitionProblemsRepository, + private val objectMapper: ObjectMapper, +) { + + + fun getAll(pageable: Pageable): Page { + return competitionProblemsRepository.findAll(pageable) + } + + fun getOne(id: Long): CompetitionsProblems { + val competitionsProblemsOptional: Optional = competitionProblemsRepository.findById(id) + return competitionsProblemsOptional.orElseThrow { + ResponseStatusException(HttpStatus.NOT_FOUND, "Entity with id `$id` not found") + } + } + + fun getMany(ids: List): List { + return competitionProblemsRepository.findAllById(ids) + } + + fun create(competitionsProblems: CompetitionsProblems): CompetitionsProblems { + return competitionProblemsRepository.save(competitionsProblems) + } + + @Throws(IOException::class) + fun patch(id: Long, patchNode: JsonNode): CompetitionsProblems { + val competitionsProblems: CompetitionsProblems = competitionProblemsRepository.findById(id).orElseThrow { + ResponseStatusException(HttpStatus.NOT_FOUND, "Entity with id `$id` not found") + } + objectMapper.readerForUpdating(competitionsProblems).readValue(patchNode) + return competitionProblemsRepository.save(competitionsProblems) + } + + @Throws(IOException::class) + fun patchMany(ids: List, patchNode: JsonNode): List { + val competitionsProblems: Collection = competitionProblemsRepository.findAllById(ids) + for (competitionsProblem in competitionsProblems) { + objectMapper.readerForUpdating(competitionsProblem).readValue(patchNode) + } + val resultCompetitionsProblems: List = + competitionProblemsRepository.saveAll(competitionsProblems) + return resultCompetitionsProblems.mapNotNull(CompetitionsProblems::id) + } + + fun delete(id: Long): CompetitionsProblems? { + val competitionsProblems: CompetitionsProblems? = competitionProblemsRepository.findById(id).orElse(null) + if (competitionsProblems != null) { + competitionProblemsRepository.delete(competitionsProblems) + } + return competitionsProblems + } + + fun deleteMany(ids: List) { + competitionProblemsRepository.deleteAllById(ids) + } + +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsProblemsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsProblemsController.kt new file mode 100644 index 0000000..7c0da77 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsProblemsController.kt @@ -0,0 +1,73 @@ +package ru.codebattles.backend.web.controllers + +import com.fasterxml.jackson.databind.JsonNode +import org.springdoc.core.annotations.ParameterObject +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.web.PagedModel +import org.springframework.web.bind.annotation.* +import ru.codebattles.backend.dto.CompetitionsProblemsDto +import ru.codebattles.backend.dto.mapper.CompetitionsProblemsMapper +import ru.codebattles.backend.entity.CompetitionsProblems +import ru.codebattles.backend.services.CompetitionsProblemsService +import java.io.IOException + +@RestController +@RequestMapping("/api/competitionsProblems") +class CompetitionsProblemsController( + private val competitionsProblemsService: CompetitionsProblemsService, + private val competitionsProblemsMapper: CompetitionsProblemsMapper, +) { + @Deprecated("Dont use global getter") + @GetMapping + fun getAll(@ParameterObject pageable: Pageable): PagedModel { + val competitionsProblems: Page = competitionsProblemsService.getAll(pageable) + return PagedModel(competitionsProblems) + } + + @GetMapping("/{id}") + fun getOne(@PathVariable id: Long): CompetitionsProblemsDto { + + return competitionsProblemsMapper.toDto( + competitionsProblemsService.getOne(id) + ) + } + + @GetMapping("/by-ids") + fun getMany(@RequestParam ids: List): List { + return competitionsProblemsMapper.toDtoS( + competitionsProblemsService.getMany(ids) + ) + } + + @PostMapping + fun create(@RequestBody competitionsProblems: CompetitionsProblems): CompetitionsProblems { + return competitionsProblemsService.create(competitionsProblems) + } + + @PatchMapping("/{id}") + @Throws(IOException::class) + fun patch(@PathVariable id: Long, @RequestBody patchNode: JsonNode): CompetitionsProblemsDto { + return competitionsProblemsMapper.toDto( + competitionsProblemsService.patch(id, patchNode) + ) + } + + @PatchMapping + @Throws(IOException::class) + fun patchMany(@RequestParam ids: List, @RequestBody patchNode: JsonNode): List { + return competitionsProblemsService.patchMany(ids, patchNode) + } + + @DeleteMapping("/{id}") + fun delete(@PathVariable id: Long): CompetitionsProblemsDto { + return competitionsProblemsMapper.toDto( + competitionsProblemsService.delete(id)!! + ) + } + + @DeleteMapping + fun deleteMany(@RequestParam ids: List) { + competitionsProblemsService.deleteMany(ids) + } +} From b17701414e92903081bd11e7b6e20272d0083a4f Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 3 May 2025 22:42:19 +0300 Subject: [PATCH 052/117] python checker: add sandbox --- z_checkers_dockerfiles/Python.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/z_checkers_dockerfiles/Python.Dockerfile b/z_checkers_dockerfiles/Python.Dockerfile index 57e2e4f..54c832d 100644 --- a/z_checkers_dockerfiles/Python.Dockerfile +++ b/z_checkers_dockerfiles/Python.Dockerfile @@ -11,7 +11,7 @@ RUN mvn -f /home/app/pom.xml clean package # FROM alpine:20240329 -RUN apk add openjdk17-jre +RUN apk add openjdk17-jre bubblewrap WORKDIR /app @@ -28,7 +28,7 @@ RUN adduser \ ENV SERVER_ENDPOINT http://backend:8000/api/check_system_callback ENV ENV_EXECUTOR_ENABLE true -ENV ENV_EXECUTOR_RUN_COMMAND python3 +ENV ENV_EXECUTOR_RUN_COMMAND bwrap --unshare-net --tmpfs /tmp --tmpfs /home --proc /proc --dev /dev --ro-bind /usr /usr --ro-bind /bin /bin --ro-bind /sbin /sbin --ro-bind /lib /lib --ro-bind /etc/passwd /etc/passwd --ro-bind /etc/group /etc/group --clearenv --bind . /work --chdir /work python3 ENV ENV_EXECUTOR_FILENAME main.py RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python From 1ac86351caf6f8ae7cdb13cd011f73274908b050 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 3 May 2025 22:59:14 +0300 Subject: [PATCH 053/117] backend: Better errors responses --- .../backend/core/config/SecurityConfig.kt | 8 +++++--- .../web/controllers/GlobalExceptionHandler.kt | 15 +++++++++++++++ .../backend/web/entity/RenderedError.kt | 5 +++++ 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/GlobalExceptionHandler.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/RenderedError.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt index 8c65388..b95fce5 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt @@ -66,18 +66,20 @@ class SecurityConfig( } } .authorizeRequests() - .requestMatchers("/api/auth/**").permitAll() +// .requestMatchers("/api/auth/**").permitAll() .requestMatchers( "/api/auth/login", "/api/auth/register", - "/api/**", "/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html", "/favicon.ico", "/webjars/**" ).permitAll() - .anyRequest().authenticated() + .requestMatchers( + "/api/**" + ).authenticated() +// .anyRequest().permitAll() .and() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) return http.build() diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/GlobalExceptionHandler.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/GlobalExceptionHandler.kt new file mode 100644 index 0000000..a977f75 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/GlobalExceptionHandler.kt @@ -0,0 +1,15 @@ +package ru.codebattles.backend.web.controllers + +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ControllerAdvice +import org.springframework.web.bind.annotation.ExceptionHandler +import ru.codebattles.backend.web.entity.RenderedError + +@ControllerAdvice +class GlobalExceptionHandler { + @ExceptionHandler(NoSuchElementException::class) + fun handleNoSuchElementException(ex: NoSuchElementException?): ResponseEntity { + return ResponseEntity(RenderedError(detail = "Not found"), HttpStatus.NOT_FOUND) + } +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/RenderedError.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/RenderedError.kt new file mode 100644 index 0000000..36c42ac --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/RenderedError.kt @@ -0,0 +1,5 @@ +package ru.codebattles.backend.web.entity + +data class RenderedError( + val detail: String +) From b1442e6d8820836338c7c6af33df68f3d01e2ebd Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 3 May 2025 23:15:03 +0300 Subject: [PATCH 054/117] backend: fix documentation --- .../backend/web/controllers/GlobalExceptionHandler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/GlobalExceptionHandler.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/GlobalExceptionHandler.kt index a977f75..0ee1925 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/GlobalExceptionHandler.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/GlobalExceptionHandler.kt @@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice import org.springframework.web.bind.annotation.ExceptionHandler import ru.codebattles.backend.web.entity.RenderedError -@ControllerAdvice +//@ControllerAdvice class GlobalExceptionHandler { @ExceptionHandler(NoSuchElementException::class) fun handleNoSuchElementException(ex: NoSuchElementException?): ResponseEntity { From 5c76f30908707c1c88405a9627361cd564c4b87b Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sat, 3 May 2025 23:51:26 +0300 Subject: [PATCH 055/117] tests: edit autotests --- tests/AutoTests.postman_collection.json | 264 +++++++++++++++ tests/autotests.json | 424 ------------------------ 2 files changed, 264 insertions(+), 424 deletions(-) create mode 100644 tests/AutoTests.postman_collection.json delete mode 100644 tests/autotests.json diff --git a/tests/AutoTests.postman_collection.json b/tests/AutoTests.postman_collection.json new file mode 100644 index 0000000..d833d8e --- /dev/null +++ b/tests/AutoTests.postman_collection.json @@ -0,0 +1,264 @@ +{ + "info": { + "_postman_id": "4184975a-28c2-4b87-8cb7-2844832a980d", + "name": "AutoTests", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "24756275" + }, + "item": [ + { + "name": "0 Basic", + "item": [ + { + "name": "0 Ping", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.have.status(200);\r", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "*/*" + } + ], + "url": { + "raw": "{{baseUrl}}/api/ping", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "ping" + ] + } + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "*/*" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"\",\n \"username\": \"\"\n}", + "options": { + "raw": { + "headerFamily": "json", + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/auth/login", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "auth", + "login" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "{\n \"token\": \"\"\n}" + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "pm.test(\"Response time is less than 100ms\", function () {\r", + " pm.expect(pm.response.responseTime).to.be.below(100);\r", + "});" + ] + } + } + ] + }, + { + "name": "1 Basic", + "item": [ + { + "name": "1 Login as admin", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.have.status(200);\r", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "*/*" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"admin\",\n \"username\": \"admin\"\n}", + "options": { + "raw": { + "headerFamily": "json", + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/auth/login", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "auth", + "login" + ] + } + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "*/*" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"\",\n \"username\": \"\"\n}", + "options": { + "raw": { + "headerFamily": "json", + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/auth/login", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "auth", + "login" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "{\n \"token\": \"\"\n}" + } + ] + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "http://localhost:8080", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/tests/autotests.json b/tests/autotests.json deleted file mode 100644 index 97f6b47..0000000 --- a/tests/autotests.json +++ /dev/null @@ -1,424 +0,0 @@ -{ - "info": { - "_postman_id": "867823eb-4831-40e5-9af1-f3f1ff1da5d1", - "name": "Auto Tests", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "24756275", - "_collection_link": "https://martian-astronaut-975194.postman.co/workspace/Codebattles~ac8c92c5-94b1-4349-a4b4-70c488f6046d/collection/24756275-867823eb-4831-40e5-9af1-f3f1ff1da5d1?action=share&source=collection_link&creator=24756275" - }, - "item": [ - { - "name": "0 Prepare enviorement", - "item": [ - { - "name": "0.1 Login to teacher panel", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"login\": \"admin\",\r\n \"password\": \"admin\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:2500/api/teacher/auth", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "2500", - "path": [ - "api", - "teacher", - "auth" - ] - } - }, - "response": [] - }, - { - "name": "0.2 Create champ", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"name\": \"aboba\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{url}}/api/teacher/champs", - "host": [ - "{{url}}" - ], - "path": [ - "api", - "teacher", - "champs" - ] - } - }, - "response": [] - }, - { - "name": "0.3 Assign problem to champ", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"problem\": \"A\",\r\n \"problem_id\": \"1\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{url}}/api/teacher/champs/:id", - "host": [ - "{{url}}" - ], - "path": [ - "api", - "teacher", - "champs", - ":id" - ], - "variable": [ - { - "key": "id", - "value": "1" - } - ] - } - }, - "response": [] - }, - { - "name": "0.4 Create user", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"users\": \"admin\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{url}}/api/teacher/champs/:id/add_users", - "host": [ - "{{url}}" - ], - "path": [ - "api", - "teacher", - "champs", - ":id", - "add_users" - ], - "variable": [ - { - "key": "id", - "value": "1" - } - ] - } - }, - "response": [] - }, - { - "name": "0.5 Save user", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "const jsonData = pm.response.json();\r", - "pm.environment.set(\"GEN_user_login\", jsonData[0].login);\r", - "pm.environment.set(\"GEN_user_password\", jsonData[0].password);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{url}}/api/teacher/champs/:id/users", - "host": [ - "{{url}}" - ], - "path": [ - "api", - "teacher", - "champs", - ":id", - "users" - ], - "variable": [ - { - "key": "id", - "value": "1" - } - ] - } - }, - "response": [] - } - ] - }, - { - "name": "1. Login", - "item": [ - { - "name": "1.2 Invalid credential tests", - "item": [ - { - "name": "1.2.2 Login test (Invalid id)", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "console.log(pm.request)\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"id\": \"1000\",\r\n \"login\": \"{{GEN_user_login}}\",\r\n \"password\": \"{{GEN_user_password}}\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{url}}/api/login", - "host": [ - "{{url}}" - ], - "path": [ - "api", - "login" - ] - } - }, - "response": [] - }, - { - "name": "1.2.3 Login test (Invalid login)", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "console.log(pm.request)\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"id\": \"1\",\r\n \"login\": \"{{GEN_user_login}}{{GEN_user_login}}\",\r\n \"password\": \"{{GEN_user_password}}\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{url}}/api/login", - "host": [ - "{{url}}" - ], - "path": [ - "api", - "login" - ] - } - }, - "response": [] - }, - { - "name": "1.2.4 Login test (Invalid password)", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "console.log(pm.request)\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"id\": \"1\",\r\n \"login\": \"{{GEN_user_login}}\",\r\n \"password\": \"{{GEN_user_password}}{{GEN_user_password}}\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{url}}/api/login", - "host": [ - "{{url}}" - ], - "path": [ - "api", - "login" - ] - } - }, - "response": [] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "packages": {}, - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "packages": {}, - "exec": [ - "pm.test(\"Status code is 403\", function () {\r", - " pm.response.to.have.status(403);\r", - "});\r", - "\r", - "\r", - "pm.test(\"Success field equals false test\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData.success).to.eql(false);\r", - "});\r", - "" - ] - } - } - ] - }, - { - "name": "1.2.1 Login test", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.have.status(200);\r", - "});\r", - "\r", - "pm.test(\"Success field test\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData.success).to.eql(true);\r", - "});\r", - "\r", - "console.log(pm.request)\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"id\": \"1\",\r\n \"login\": \"{{GEN_user_login}}\",\r\n \"password\": \"{{GEN_user_password}}\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{url}}/api/login", - "host": [ - "{{url}}" - ], - "path": [ - "api", - "login" - ] - } - }, - "response": [] - } - ] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "packages": {}, - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "packages": {}, - "exec": [ - "pm.test(\"Content-Type is present\", function () {\r", - " pm.response.to.have.header(\"Content-Type\");\r", - "});\r", - "\r", - "pm.test(\"Response time is less than 500ms\", function () {\r", - " pm.expect(pm.response.responseTime).to.be.below(500);\r", - "});" - ] - } - } - ] -} \ No newline at end of file From 775976aa60d0fe1cf0cfe62a0c44be4c0590f4d5 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 9 May 2025 14:25:02 +0300 Subject: [PATCH 056/117] backend: add endpoints and fix responses --- .../backend/core/config/SecurityConfig.kt | 5 +++-- .../backend/services/CompetitionService.kt | 10 ++++++++++ .../services/CompetitionsProblemsService.kt | 13 ++++++++++++- .../backend/services/ProblemsService.kt | 10 ++++++++++ .../CompetitionsProblemsController.kt | 9 +++++++-- .../web/controllers/PingPongController.kt | 19 +++++++++++++++++++ .../web/entity/CreateCompetitionProblem.kt | 8 ++++++++ 7 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/PingPongController.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/CreateCompetitionProblem.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt index b95fce5..62a3afd 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt @@ -5,7 +5,6 @@ import org.springframework.context.annotation.Configuration import org.springframework.security.authentication.AuthenticationManager import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter @@ -59,7 +58,7 @@ class SecurityConfig( cors.configurationSource { request -> CorsConfiguration().applyPermitDefaultValues().also { it.allowedOriginPatterns = listOf("*") - it.allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS") + it.allowedMethods = listOf("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") it.allowedHeaders = listOf("*") it.allowCredentials = true } @@ -68,8 +67,10 @@ class SecurityConfig( .authorizeRequests() // .requestMatchers("/api/auth/**").permitAll() .requestMatchers( + "/api/ping", "/api/auth/login", "/api/auth/register", + "/api/check_system_callback/**", "/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html", diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt index 047eece..f331830 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionService.kt @@ -47,6 +47,16 @@ class CompetitionService( throw ResponseStatusException(HttpStatus.NOT_FOUND) } + fun getByIdNotDto(id: Long): Competition { + val optionalCompetition = competitionRepository.findById(id) + if (optionalCompetition.isPresent) { + return optionalCompetition.get() + } + + + throw ResponseStatusException(HttpStatus.NOT_FOUND) + } + fun getProblemsById(id: Long): List { return competitionsProblemsMapper.toDtoS( competitionProblemsRepository.getAllByCompetitionId(id) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt index 97acaea..1675b37 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt @@ -9,6 +9,7 @@ import org.springframework.stereotype.Service import org.springframework.web.server.ResponseStatusException import ru.codebattles.backend.entity.CompetitionsProblems import ru.codebattles.backend.repository.CompetitionProblemsRepository +import ru.codebattles.backend.web.entity.CreateCompetitionProblem import java.io.IOException import java.util.* @@ -16,6 +17,8 @@ import java.util.* class CompetitionsProblemsService( private val competitionProblemsRepository: CompetitionProblemsRepository, private val objectMapper: ObjectMapper, + private val competitionService: CompetitionService, + private val problemsService: ProblemsService, ) { @@ -34,7 +37,15 @@ class CompetitionsProblemsService( return competitionProblemsRepository.findAllById(ids) } - fun create(competitionsProblems: CompetitionsProblems): CompetitionsProblems { + fun create(data: CreateCompetitionProblem): CompetitionsProblems { + + val competitionsProblems = CompetitionsProblems( + priority = data.priority.toInt(), + slug = data.slug, + competition = competitionService.getByIdNotDto(data.competition_id), + problem = problemsService.getByIdNotDto(data.problem_id), + ) + return competitionProblemsRepository.save(competitionsProblems) } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt index 2b5a8f1..4494806 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/ProblemsService.kt @@ -26,6 +26,16 @@ class ProblemsService( throw ResponseStatusException(HttpStatus.NOT_FOUND) } + fun getByIdNotDto(id: Long): Problem { + val optionalProblem = problemsRepository.findById(id) + if (optionalProblem.isPresent) { + return optionalProblem.get() + } + + + throw ResponseStatusException(HttpStatus.NOT_FOUND) + } + fun create(problemDto: CreateProblemDto): ProblemDto { val problem = Problem( diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsProblemsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsProblemsController.kt index 7c0da77..ab12011 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsProblemsController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsProblemsController.kt @@ -1,6 +1,7 @@ package ru.codebattles.backend.web.controllers import com.fasterxml.jackson.databind.JsonNode +import io.swagger.v3.oas.annotations.security.SecurityRequirement import org.springdoc.core.annotations.ParameterObject import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable @@ -10,10 +11,12 @@ import ru.codebattles.backend.dto.CompetitionsProblemsDto import ru.codebattles.backend.dto.mapper.CompetitionsProblemsMapper import ru.codebattles.backend.entity.CompetitionsProblems import ru.codebattles.backend.services.CompetitionsProblemsService +import ru.codebattles.backend.web.entity.CreateCompetitionProblem import java.io.IOException @RestController @RequestMapping("/api/competitionsProblems") +@SecurityRequirement(name = "JWT") class CompetitionsProblemsController( private val competitionsProblemsService: CompetitionsProblemsService, private val competitionsProblemsMapper: CompetitionsProblemsMapper, @@ -41,8 +44,10 @@ class CompetitionsProblemsController( } @PostMapping - fun create(@RequestBody competitionsProblems: CompetitionsProblems): CompetitionsProblems { - return competitionsProblemsService.create(competitionsProblems) + fun create(@RequestBody data: CreateCompetitionProblem): CompetitionsProblemsDto { + return competitionsProblemsMapper.toDto( + competitionsProblemsService.create(data) + ) } @PatchMapping("/{id}") diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/PingPongController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/PingPongController.kt new file mode 100644 index 0000000..285dd0c --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/PingPongController.kt @@ -0,0 +1,19 @@ +package ru.codebattles.backend.web.controllers + +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import ru.codebattles.backend.web.entity.RenderedError + +@RestController +@RequestMapping("/api/ping") +class PingPongController( +) { + + @GetMapping + fun ping(): RenderedError { + return RenderedError(detail = "pong") + } + + +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/CreateCompetitionProblem.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/CreateCompetitionProblem.kt new file mode 100644 index 0000000..b87aaad --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/CreateCompetitionProblem.kt @@ -0,0 +1,8 @@ +package ru.codebattles.backend.web.entity + +class CreateCompetitionProblem( + val priority: Long, + val slug: String, + val competition_id: Long, + val problem_id: Long, +) From 43ac3c63f0f10d5e166977be64cbd51910e06469 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 9 May 2025 14:30:29 +0300 Subject: [PATCH 057/117] frontend: add admin panel edit champs --- FRONTEND_V2/src/App.jsx | 8 ++ FRONTEND_V2/src/components/DeleteButton.jsx | 51 ++++++++ .../src/pages/admin/AdminChampsCreate.jsx | 112 ++++++++++++++++++ .../src/pages/admin/AdminChampsDetailPage.jsx | 2 +- .../AdminChampsDetailProblemsEditPage.jsx | 98 +++++++++++++++ .../AdminChampsDetailProblemsLinkPage.jsx | 105 ++++++++++++++++ .../admin/AdminChampsDetailProblemsPage.jsx | 82 +++++++++++++ .../src/pages/admin/AdminChampsPage.jsx | 4 + 8 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 FRONTEND_V2/src/components/DeleteButton.jsx create mode 100644 FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx create mode 100644 FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsEditPage.jsx create mode 100644 FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsLinkPage.jsx create mode 100644 FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsPage.jsx diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index e0f346b..f9dc6b0 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -23,6 +23,10 @@ import {AdminChampsDetailRatingPage} from "./pages/admin/AdminChampsDetailRating import {AdminSeeSendPage} from "./pages/admin/AdminSeeSendPage.jsx"; import {NotFound} from "./pages/NotFound.jsx"; import {AdminUsersDetailPage} from "./pages/admin/AdminUsersDetailPage.jsx"; +import {AdminChampsCreate} from "./pages/admin/AdminChampsCreate.jsx"; +import {AdminChampsDetailProblemsPage} from "./pages/admin/AdminChampsDetailProblemsPage.jsx"; +import {AdminChampsDetailProblemsLinkPage} from "./pages/admin/AdminChampsDetailProblemsLinkPage.jsx"; +import {AdminChampsDetailProblemsEditPage} from "./pages/admin/AdminChampsDetailProblemsEditPage.jsx"; function App() { @@ -48,7 +52,11 @@ function App() { }/> }/> }/> + }/> }/> + }/> + }/> + }/> }/> }/> }/> diff --git a/FRONTEND_V2/src/components/DeleteButton.jsx b/FRONTEND_V2/src/components/DeleteButton.jsx new file mode 100644 index 0000000..df77958 --- /dev/null +++ b/FRONTEND_V2/src/components/DeleteButton.jsx @@ -0,0 +1,51 @@ +import React, {useState} from 'react'; +import constants from "../utils/consts.js"; +import axios from "axios"; +import {useNavigate} from "react-router-dom"; + +export const DeleteButton = ({ + url, + callback = () => { + } + }) => { + + const [disabled, setDisabled] = useState(false) + + const navigate = useNavigate() + + const conf = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` + } + } + + const onClick = () => { + setDisabled(true) + + axios.delete(url, conf) + .then(() => navigate("/admin/champs")) + .finally(() => setDisabled(false)) + } + + return ( + + ); +}; + diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx new file mode 100644 index 0000000..3b7d79b --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx @@ -0,0 +1,112 @@ +import Card from "../../components/bootstrap/Card.jsx"; +import {useState} from "react"; +import {useNavigate} from "react-router-dom"; +import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; +import {useForm} from "react-hook-form"; +import axios from "axios"; +import constants from "../../utils/consts.js"; + +export const AdminChampsCreate = () => { + + const {register, handleSubmit, formState: {errors}} = useForm({ + defaultValues: { + name: '', + description: '', + startedAt: new Date().toISOString().slice(0, 16), + endedAt: new Date().toISOString().slice(0, 16) + } + }); + const [submittedData, setSubmittedData] = useState(null); + + const navigate = useNavigate(); + + const onSubmit = (data) => { + // Convert date strings to ISO format + const formattedData = { + ...data, + startedAt: new Date(data.startedAt).toISOString(), + endedAt: new Date(data.endedAt).toISOString() + }; + + console.log(formattedData); + setSubmittedData(formattedData); + + const conf = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` + } + } + + axios.post('/api/competitions', data, conf) + .then(() => navigate("/admin/champs")) + }; + + + return ( + <> + + + + + + + + + +
+
+ + + {errors.name &&

{errors.name.message}

} +
+ +
+ + - - - -
- - - \ No newline at end of file diff --git a/BACKEND/src/templates/admin/auth.html b/BACKEND/src/templates/admin/auth.html deleted file mode 100644 index 7e607b2..0000000 --- a/BACKEND/src/templates/admin/auth.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - Title - - - - - - - \ No newline at end of file diff --git a/BACKEND/src/templates/admin/create.html b/BACKEND/src/templates/admin/create.html deleted file mode 100644 index 350dfa3..0000000 --- a/BACKEND/src/templates/admin/create.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - Document - - - - -
-

Админ панель

-
- -
-

Создать соревнование

-
- - - - - - -
- -
-
- - - \ No newline at end of file diff --git a/BACKEND/src/templates/admin/panel.html b/BACKEND/src/templates/admin/panel.html deleted file mode 100644 index 145d950..0000000 --- a/BACKEND/src/templates/admin/panel.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - Document - - - - -
-

Админ панель

- Создать соревнование -
- - {% for champ in champs %} - -
-
-

{{champ[0]}}

-

{{champ[1]}}

-

        

- Настройки - Участники -
-
-

ХЗ человек

-

Старт в {{champ[2]}}

-

Конец в ХЗ

-
-
- - {% endfor %} - - - - \ No newline at end of file diff --git a/BACKEND/src/templates/admin/problems_list.html b/BACKEND/src/templates/admin/problems_list.html deleted file mode 100644 index 8f22b6d..0000000 --- a/BACKEND/src/templates/admin/problems_list.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - Document - - - - -
-

Админ панель

-
- -
-

Добавить соревнование

- Из TestsGeneratorFramework -
- -

build.json файл

- - -
-
- - -

Соревнования

- - -
- - {% for problem in problems%} - -
-
-

{{problem[0]}}

-

{{problem[1]}}

- -
-
- - {% endfor %} - -
- - - \ No newline at end of file diff --git a/BACKEND/src/templates/admin/settings.html b/BACKEND/src/templates/admin/settings.html deleted file mode 100644 index 32ad5ce..0000000 --- a/BACKEND/src/templates/admin/settings.html +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - Document - - - - -
-

Админ панель

-
-

ID

-

{{id}}

-

{{name}}

-

Тестоовое супер нужное и важное название

-
-
- - Управление пользователями - -
- - - - - - - - - - - {% for task in tasks %} - - - - - - {% endfor %} - -
Букваid задачиНазвание задачи
{{task[0]}}{{task[1]}}{{task[2]}}
-
-
- -
- - - - - -

Поставьте id -1 чтобы удалить задачу

- -
-
- - - \ No newline at end of file diff --git a/BACKEND/src/templates/admin/users.html b/BACKEND/src/templates/admin/users.html deleted file mode 100644 index 4b69b70..0000000 --- a/BACKEND/src/templates/admin/users.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - Document - - - - -
-

Админ панель

-
-

ID

-

{{id}}

-
-
- - Добавить пользователей - -
- - - - - - - - - - - {% for task in users %} - - - - - - {% endfor %} - -
ИмяЛогинПароль
{{task[0]}}{{task[1]}}{{task[2]}}
-
- - - \ No newline at end of file diff --git a/BACKEND/src/utils.py b/BACKEND/src/utils.py deleted file mode 100644 index f032ce0..0000000 --- a/BACKEND/src/utils.py +++ /dev/null @@ -1,46 +0,0 @@ -import re -from hashlib import sha256 -import env - - -def salt_crypt(*args): - args = list(map(str, args)) - payload = f"{env.HASH_SALT}+++{str(args)}" - return sha256(payload.encode("utf-8")).hexdigest() - - -def fix_new_line(data): - print(data) - if isinstance(data, list): - out = [] - for i in data: - out.append(fix_new_line(i)) - return out - elif isinstance(data, str): - return data.replace("\\n", "\n") - else: - return data - - -def get_table_color_class_by_score(score): - if score is None: - return "" - elif score == 100: - return "table-success" - - elif score == 0: - return "table-danger" - - return "table-warning" - - -def get_table_color_class_by_test_message(msg): - if msg == "OK" or msg == "SUCCESS": - return "table-success" - elif msg == "WRONG_ANSWER": - return "table-danger" - - return "table-info" - - -LETTER_REGEX = re.compile(r"[A-Z]") diff --git a/BACKEND/src/web/__init__.py b/BACKEND/src/web/__init__.py deleted file mode 100644 index baa374a..0000000 --- a/BACKEND/src/web/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import web.api -import web.teacher_api -import web.admin - -import web.solution_processing -import web.error_handlers diff --git a/BACKEND/src/web/admin/__init__.py b/BACKEND/src/web/admin/__init__.py deleted file mode 100644 index 392ff91..0000000 --- a/BACKEND/src/web/admin/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import web.admin.panel -import web.admin.auth -import web.admin.problems diff --git a/BACKEND/src/web/admin/auth.py b/BACKEND/src/web/admin/auth.py deleted file mode 100644 index 53278d4..0000000 --- a/BACKEND/src/web/admin/auth.py +++ /dev/null @@ -1,21 +0,0 @@ -from flask import render_template, request, redirect, make_response - -from app import app - - -@app.route("/admin/auth") -def admin_auth(): - return render_template("admin/auth.html") - - -@app.route("/admin/auth", methods=["POST"]) -def admin_auth_post(): - login, password = request.form["login"], request.form["password"] - - resp = make_response(redirect("/admin")) - - resp.set_cookie("admin", f"{login}_{password}_531") - - print(login, password) - - return resp diff --git a/BACKEND/src/web/admin/panel.py b/BACKEND/src/web/admin/panel.py deleted file mode 100644 index 275e449..0000000 --- a/BACKEND/src/web/admin/panel.py +++ /dev/null @@ -1,171 +0,0 @@ -import random -import string - -from flask import render_template, redirect, request - -from app import app -from database import get_connection -from database.createTables import get_query_users_table, get_query_sends_table -from decorators import admin_required - - -@app.route("/admin") -@admin_required -def admin_panel(): - connection = get_connection() - cursor = connection.cursor() - - cursor.execute("SELECT id, name, started FROM champs ") - - champs = cursor.fetchall() - champs = list(map(lambda x: [*x], champs)) - - return render_template("admin/panel.html", champs=champs) - - -@app.route("/admin/champ/") -@admin_required -def settings(champ_id): - connection = get_connection() - cur = connection.cursor() - - cur.execute("SELECT * FROM champs WHERE id = %s", (str(champ_id),)) - - fetch = cur.fetchone() # Can be None - problems_ids_temp = fetch[3:] - problems_ids = [] - - sql = "SELECT * FROM problems WHERE id = -1 " - - strs = string.ascii_uppercase - - for task in problems_ids_temp: - if task is not None: - problems_ids.append(task) - sql += "OR id = %s " - - cur.execute(sql, tuple(problems_ids)) - - tasks_dict = dict.fromkeys(problems_ids) - - x = list(cur.fetchall()) - - tasks = [] - - for i, task in enumerate(x): - task_id = task[0] - name = task[1] - tasks_dict[task_id] = task - tasks.append((strs[problems_ids.index(task_id)], task_id, name)) - - tasks.sort() - - print() - - return render_template( - "admin/settings.html", tasks=tasks, id=fetch[0], name=fetch[1] - ) - - -@app.route("/admin/champ/", methods=["POST"]) -@admin_required -def settings_post(champ_id): - connection = get_connection() - cur = connection.cursor() - - form = request.form - problem = form["problem"] - problem_id = form["problem_id"] - - if problem_id == "": - return redirect(f"/admin/champ/{champ_id}") - problem_id = int(problem_id) - - print(problem, problem_id) - - cur.execute(f"""UPDATE champs SET {problem} = {problem_id} WHERE id = {champ_id}""") - - connection.commit() - - return redirect(f"/admin/champ/{champ_id}") - - -@app.route("/admin/create/") -@admin_required -def create_champ(): - return render_template("admin/create.html") - - -@app.route("/admin/create/", methods=["POST"]) -@admin_required -def create_champ_post(): - name = request.form["name"] - - connection = get_connection() - cur = connection.cursor() - - cur.execute("INSERT INTO champs (name) VALUES (%s)", (name,)) - - connection.commit() - - cur.execute("SELECT currval(pg_get_serial_sequence('champs','id'));") - champ_id = cur.fetchone()[0] - - cur.execute(get_query_users_table(champ_id)) - cur.execute(get_query_sends_table(champ_id)) - - connection.commit() - - return redirect("/admin") - - -@app.route("/admin/champ//add_users") -@admin_required -def create_users_in_champ(champ_id): - return render_template("admin/add_users.html") - - -@app.route("/admin/champ//add_users", methods=["POST"]) -@admin_required -def create_users_in_champ_post(champ_id): - users = request.form["users"].split("\r\n") - - connection = get_connection() - cursor = connection.cursor() - - for name in users: - # login = translit(name, 'ru', reversed=True) - # login = login.replace(" ", "") - # login = login.replace("'", "") - # if len(login) > 7: - # login = login[:6] - # - # login = f"{login}{random.randint(10, 99)}" - # - # password = get_random_string(8) - - login = "".join(map(str, [random.randint(0, 9) for _ in range(5)])) - password = "".join(map(str, [random.randint(0, 9) for _ in range(5)])) - - cursor.execute( - f"INSERT INTO champUsers_{champ_id} (login, password, name)" - f" VALUES (%s, %s, %s)", - (login, password, name), - ) - - connection.commit() - - return redirect(f"/admin/champ/{champ_id}/users") - - -@app.route("/admin/champ//users") -@admin_required -def users_route(champ_id): - connection = get_connection() - cur = connection.cursor() - - cur.execute(f"SELECT name, login, password FROM champUsers_{champ_id}") - - users = cur.fetchall() - - return render_template("admin/users.html", users=users, id=champ_id) diff --git a/BACKEND/src/web/admin/problems.py b/BACKEND/src/web/admin/problems.py deleted file mode 100644 index deee7b3..0000000 --- a/BACKEND/src/web/admin/problems.py +++ /dev/null @@ -1,63 +0,0 @@ -import json - -from flask import render_template, request - -from app import app -from database import get_connection -from decorators import admin_required - - -@app.route("/admin/problems") -@admin_required -def admin_list_problems(): - connection = get_connection() - cursor = connection.cursor() - - cursor.execute("SELECT id, name, description FROM problems") - - problems = cursor.fetchall() - - problems = list(map(lambda problem: [*problem], problems)) - - return render_template("admin/problems_list.html", problems=problems) - - -@app.route("/admin/problems/add", methods=["POST"]) -@admin_required -def admin_list_problems_add(): - connection = get_connection() - cursor = connection.cursor() - - cursor.execute("SELECT id, name, description FROM problems") - - problems = cursor.fetchall() - - problems = list(map(lambda problem: [*problem], problems)) - - build = request.form["build"] - try: - build_json = json.loads(build) - build_json["tests"] = json.dumps(build_json["tests"]) - build_json["examples"] = json.dumps(build_json["examples"]) - except Exception as e: - print("Maybe Json parse exception \n" + str(e)) - return "Not json (404 ERR)", 400 - - print(build) - print(build_json) - - cursor.execute( - """INSERT INTO problems (name, description, "in", out, examples, tests) VALUES (%s, %s, %s, %s, %s, %s) """, - ( - build_json["name"], - build_json["description"], - build_json["in"], - build_json["out"], - build_json["examples"], - build_json["tests"], - ), - ) - - connection.commit() - - return render_template("admin/problems_list.html", problems=problems) diff --git a/BACKEND/src/web/api/__init__.py b/BACKEND/src/web/api/__init__.py deleted file mode 100644 index 6d7d143..0000000 --- a/BACKEND/src/web/api/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import web.api.send_prog -import web.api.sends -import web.api.battle -import web.api.quiz_problems -import web.api.auth diff --git a/BACKEND/src/web/api/auth.py b/BACKEND/src/web/api/auth.py deleted file mode 100644 index 11ad6f9..0000000 --- a/BACKEND/src/web/api/auth.py +++ /dev/null @@ -1,54 +0,0 @@ -from flask import request, make_response - -from app import app -from database import get_connection -from decorators.validation import json_validate -from utils import salt_crypt -from web.validation_form.api import LoginForm - - -@app.route("/api/logout", methods=["POST", "GET"]) -def logout_api(): - resp = make_response({"success": True}) - - resp.set_cookie("user_id", expires=0) - resp.set_cookie("authed", expires=0) - resp.set_cookie("battle_id", expires=0) - - return resp - - -@app.route("/api/login", methods=["POST"]) -@json_validate(LoginForm) -def login_post_api(data: LoginForm): - try: - champ_id = data.id.data - login = data.login.data - password = data.password.data - - champ_id = int(champ_id) - - con = get_connection() - cur = con.cursor() - cur.execute( - f"SELECT * FROM public.champUsers_{champ_id}" - f" WHERE login = %s AND password = %s", - (login, password), - ) - user = cur.fetchone() - - assert user is not None - - user_id = str(user[0]) - - resp = make_response({"success": True}) - resp.set_cookie("user_id", user_id) - resp.set_cookie("authed", str(True)) - resp.set_cookie("battle_id", str(champ_id)) - resp.set_cookie("__validation", salt_crypt(champ_id, user_id)) - - return resp - except Exception as e: - print(e) - - return {"success": False, "msg": "Bad Credentials"}, 403 diff --git a/BACKEND/src/web/api/battle.py b/BACKEND/src/web/api/battle.py deleted file mode 100644 index 7b66ec1..0000000 --- a/BACKEND/src/web/api/battle.py +++ /dev/null @@ -1,220 +0,0 @@ -import json -import re -import string - -from flask import abort - -from app import app -from database import get_connection -from decorators import get_user_id, api_login_required, redis_conn -from utils import fix_new_line, get_table_color_class_by_score, LETTER_REGEX - -JSON_MIMETYPE = "application/json" - - -@app.route("/api/problems") -@api_login_required -@get_user_id -def api_problems(user_id, uid): - connection = get_connection() - cur = connection.cursor() - - cur.execute("SELECT * FROM champs WHERE id = %s", (str(user_id),)) - - fetch = cur.fetchone() # Can be None - problems_ids_temp = fetch[3:] - problems_ids = [] - - sql = "SELECT * FROM problems WHERE id = -1 " - - strs = string.ascii_uppercase - - for task in problems_ids_temp: - if task is not None: - problems_ids.append(task) - sql += "OR id = %s " - - cur.execute(sql, tuple(problems_ids)) - - tasks_dict = dict.fromkeys(problems_ids) - - x = list(cur.fetchall()) - - tasks = {} - is_quizes = {} - - cur.execute(f"SELECT * FROM champUsers_{user_id} WHERE id = %s", (uid,)) - - fetch = cur.fetchone() # Can be None - - score = fetch[4 : 4 + len(problems_ids)] - - css_colors = {} - - for i, task in enumerate(x): - id = task[0] - name = task[1] - is_quiz = task[-1] - if is_quiz is None: - is_quiz = False - - tasks_dict[id] = task - - letter = strs[problems_ids.index(id)] - - tasks[letter] = name - is_quizes[letter] = is_quiz - - css_colors[letter] = get_table_color_class_by_score(score[strs.index(letter)]) - - print() - - return { - "success": "true", - "problems": tasks, - "colors": css_colors, - "is_quizes": is_quizes, - } - - -@app.route("/api/problem/") -@api_login_required -def api_problem(letter, user_id): - if not re.fullmatch(LETTER_REGEX, letter): - return abort(404) - - connection = get_connection() - cur = connection.cursor() - - cur.execute("SELECT * FROM champs WHERE id = %s", (str(user_id),)) - - fetch = cur.fetchone() # Can be None - problems_ids_temp = fetch[3:] - problems_ids = [] - - sql = "SELECT * FROM problems WHERE id = -1 " - - strs = string.ascii_uppercase - - for i in problems_ids_temp: - if i is not None: - problems_ids.append(i) - sql += "OR id = %s " - - cur.execute(sql, tuple(problems_ids)) - - x = list(cur.fetchall()) - - problem_ = None - - for i in x: - problem_id = i[0] - problem_letter = strs[problems_ids.index(problem_id)] - if letter == problem_letter: - problem_ = i - - if problem_ is None: - abort(404) - - cur.execute("SELECT name, id FROM servers WHERE enabled=true") - servers = cur.fetchall() - - _pr_id, _pr_name, _pr_desc, _pr_in, _pr_out, _pr_tests, _pr_examples, _ = problem_ - - _pr_examples = fix_new_line(json.loads(_pr_examples)) - - langs = {} - - for i in servers: - langs[i[0]] = i[1] - - return dict( - success=True, - name=_pr_name, - description=_pr_desc, - letter=letter, - in_data=_pr_in, - out_data=_pr_out, - examples=_pr_examples, - langs=langs, - ) - - -@app.route("/api/stats") -@api_login_required -@get_user_id -@redis_conn -def api_statistics(user_id, uid, r): - champ_id = user_id - - redis_cache = r.get(f"r-champ-{champ_id}-stats") - if redis_cache is not None: - response = app.response_class( - response=redis_cache, status=200, mimetype=JSON_MIMETYPE - ) - print("redis") - return response - - connection = get_connection() - cur = connection.cursor() - - cur.execute("SELECT * FROM champs WHERE id = %s", (str(user_id),)) - - fetch = cur.fetchone() # Can be None - problems_ids_temp = fetch[3:] - problems_counts = 0 - - for i in problems_ids_temp: - if i is not None: - problems_counts += 1 - else: - break - - strs = string.ascii_uppercase - - print(problems_counts) - - cur.execute( - f""" - SELECT u.*, MAX(s.send_time) AS send_time - FROM champusers_{user_id} u - LEFT JOIN champsends_{user_id} s ON u.id = s.user_id - GROUP BY u.id - ORDER BY score DESC, send_time ASC; - """ - ) - - fetch = cur.fetchall() # Can be None - - users = [] - - for i, usr in enumerate(fetch): - user_id = usr[0] - score = usr[15] - last_send = usr[16] - last_send = ( - None if last_send is None else last_send.strftime("%m/%d/%Y, %H:%M:%S") - ) - - nickname = usr[3] - problems_score = usr[4 : problems_counts + 4] - - # problems_score = list(map(lambda s: (s, "")[s is None], problems_score)) - - users.append( - { - "position": i + 1, - "name": nickname, - "user_id": user_id, - "score": score, - "problems_score": problems_score, - "last_send": last_send, - } - ) - - print() - - resp_string = {"success": True, "cols": strs[:problems_counts], "users": users} - - r.set(f"r-champ-{champ_id}-stats", json.dumps(resp_string)) - return resp_string diff --git a/BACKEND/src/web/api/quiz_problems.py b/BACKEND/src/web/api/quiz_problems.py deleted file mode 100644 index 5d541d2..0000000 --- a/BACKEND/src/web/api/quiz_problems.py +++ /dev/null @@ -1,77 +0,0 @@ -import json -import re -import string - -from flask import abort - -from app import app -from database import get_connection -from decorators import get_user_id, api_login_required, redis_conn -from utils import fix_new_line, get_table_color_class_by_score, LETTER_REGEX - -JSON_MIMETYPE = "application/json" - - -@app.route("/api/problem//quiz") -@api_login_required -def get_quiz(letter, user_id): - # if not re.fullmatch(LETTER_REGEX, letter): - # return abort(404) - - connection = get_connection() - cur = connection.cursor() - - cur.execute("SELECT * FROM champs WHERE id = %s", (str(user_id),)) - - fetch = cur.fetchone() # Can be None - problems_ids_temp = fetch[3:] - problems_ids = [] - - sql = "SELECT * FROM problems WHERE id = -1 " - - strs = string.ascii_uppercase - - for i in problems_ids_temp: - if i is not None: - problems_ids.append(i) - sql += "OR id = %s " - - cur.execute(sql, tuple(problems_ids)) - - x = list(cur.fetchall()) - - problem_ = None - - for i in x: - problem_id = i[0] - problem_letter = strs[problems_ids.index(problem_id)] - if letter == problem_letter: - problem_ = i - - if problem_ is None: - abort(404) - - _pr_id, _pr_name, _pr_desc, _pr_in, _pr_out, _pr_tests, _pr_examples, _ = problem_ - - print(_pr_tests) - _pr_tests = json.loads(_pr_tests) - filtered_test = [] - - for question in _pr_tests["questions"]: - print(question) - filtered_test.append( - { - "id": question["id"], - "name": question["question"], - "answers": question["answers"], - "type": question["type"], - } - ) - - return dict( - success=True, - name=_pr_name, - description=_pr_desc, - letter=letter, - tests=filtered_test, - ) diff --git a/BACKEND/src/web/api/send_prog.py b/BACKEND/src/web/api/send_prog.py deleted file mode 100644 index c3abc1e..0000000 --- a/BACKEND/src/web/api/send_prog.py +++ /dev/null @@ -1,106 +0,0 @@ -import datetime -import json -import string - -import requests - -import env -from app import app -from database import get_connection -from decorators import get_user_id, api_login_required -from decorators.validation import json_validate -from web.validation_form.api import SendProgramForm - - -@app.route("/api/send", methods=["POST"]) -@api_login_required -@get_user_id -@json_validate(SendProgramForm) -def api_send_prog(user_id, uid, data: SendProgramForm): - connection = get_connection() - cur = connection.cursor() - - f_lang = data.cars.data - f_code = data.src.data - problem_letter_form = data.problem.data - - cur.execute("SELECT * FROM champs WHERE id = %s", (str(user_id),)) - - fetch = cur.fetchone() # Can be None - problems_ids_temp = fetch[3:] - problems_ids = [] - - sql = "SELECT * FROM problems WHERE id = -1 " - - strs = string.ascii_uppercase - - for i in problems_ids_temp: - if i is not None: - problems_ids.append(i) - sql += "OR id = %s " - - cur.execute(sql, tuple(problems_ids)) - - x = list(cur.fetchall()) - - problem_ = None - - for i in x: - _id = i[0] - problem_letter = strs[problems_ids.index(_id)] - if problem_letter_form == problem_letter: - problem_ = i - - tests = problem_[5] - tests = json.loads(tests) - tests = list(map(lambda z: {"in": z[0], "out": z[1]}, tests)) - - print(problem_) - - cur.execute( - f""" - INSERT INTO champSends_{user_id} - (problem_name, problem_id, user_id, send_time, state, program, problem_letter, lang) - VALUES(%s, %s, %s, %s, %s, %s, %s, %s); - """, - ( - problem_[1], - problem_[0], - uid, - datetime.datetime.now(), - "Тестируется", - f_code, - problem_letter_form, - f_lang, - ), - ) - cur.execute(f"SELECT currval(pg_get_serial_sequence('champSends_{user_id}','id'));") - - inserted_id = cur.fetchone()[0] - - meta = { - "champ_id": user_id, - "user_id": uid, - "problem": problem_letter_form, - "id": inserted_id, - } - - payload = { - "meta": json.dumps(meta), - "source": f_code, - "compiler": f_lang, - "tests": tests, - } - - connection.commit() - - cur.execute( - f"SELECT address FROM servers WHERE id = %s and enabled = true", (f_lang,) - ) - - server_addr = cur.fetchone() - server_addr = server_addr[0] - print() - - requests.post(f"http://{server_addr}:{env.CHECKER_PORT}/api/v1/test", json=payload) - return {"success": True} diff --git a/BACKEND/src/web/api/sends.py b/BACKEND/src/web/api/sends.py deleted file mode 100644 index 7ad135a..0000000 --- a/BACKEND/src/web/api/sends.py +++ /dev/null @@ -1,124 +0,0 @@ -import json - -from app import app -from database import get_connection -from decorators import get_user_id, api_login_required, redis_conn - - -@app.route("/api/sends") -@api_login_required -@get_user_id -@redis_conn -def api_sends(user_id, uid, r): - champ_id = user_id - - redis_cache = r.get(f"r-champ-{champ_id}-sends-user-{uid}") - if redis_cache is not None: - response = app.response_class( - response=redis_cache, status=200, mimetype="application/json" - ) - - return response - - connection = get_connection() - cur = connection.cursor() - - cur.execute( - f"SELECT * FROM champSends_{user_id} WHERE user_id = %s ORDER BY send_time DESC", - (uid,), - ) - - db_sends = cur.fetchall() - - to_render = [] - - for send in db_sends: - ( - id, - letter, - name, - problem_id, - pr_user_id, - send_time, - state, - result, - program, - score, - lang, - ) = send - - human_send_time = send_time.strftime("%m/%d/%Y, %H:%M:%S") - - to_render.append( - dict( - id=id, - letter=letter, - name=name, - send_time=human_send_time, - state=state, - score=(score, "")[score is None], - program_checked=result is not None, - ) - ) - - print() - - dict_resp = dict(success=True, sends=to_render) - - r.set(f"r-champ-{champ_id}-sends-user-{uid}", json.dumps(dict_resp)) - - return dict_resp - - -@app.route("/api/send/") -@api_login_required -def api_send_viewer(send_id, user_id): - connection = get_connection() - cur = connection.cursor() - - cur.execute( - f""" - SELECT t1.*, t2.name as lang_name, t2.lang_name as lang_id - FROM public.champSends_{user_id} as t1 - INNER JOIN public.servers as t2 - on CAST(t1.lang as INTEGER) = t2.id - - WHERE t1.id = %s - """, - (send_id,), - ) - - data = cur.fetchone() - - if data is None: - return {}, 404 - - result = json.loads(data[7]) - - prog = data[8] - lang = data[11] - lang_id = data[12] - - to_render = [] - - for i, test in enumerate(result): - message = test["msg"] - out = test["out"] - - if message == "WRONG_ANSWER": - out = """ВЫВОД СКРЫТ""" - - to_add = {"id": i + 1, "time": test["time"], "msg": message, "out": out} - to_render.append(to_add) - - connection.close() - - print() - - return { - "success": True, - "tests": to_render, - "lang": lang, - "program": prog, - "lang_id": lang_id, - } diff --git a/BACKEND/src/web/error_handlers.py b/BACKEND/src/web/error_handlers.py deleted file mode 100644 index b3507fe..0000000 --- a/BACKEND/src/web/error_handlers.py +++ /dev/null @@ -1,9 +0,0 @@ -from app import app - - -@app.errorhandler(404) -@app.errorhandler(403) -@app.errorhandler(405) -@app.errorhandler(500) -def resource_not_found(e): - return {"success": False, "status": e.code, "error": str(e)}, e.code diff --git a/BACKEND/src/web/solution_processing/__init__.py b/BACKEND/src/web/solution_processing/__init__.py deleted file mode 100644 index 881f412..0000000 --- a/BACKEND/src/web/solution_processing/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -import web.solution_processing.checker_api -import web.solution_processing.quiz_api diff --git a/BACKEND/src/web/solution_processing/checker_api.py b/BACKEND/src/web/solution_processing/checker_api.py deleted file mode 100644 index e21ab11..0000000 --- a/BACKEND/src/web/solution_processing/checker_api.py +++ /dev/null @@ -1,160 +0,0 @@ -import json -import re -import string -import datetime - -import requests -from flask import redirect, request - -from app import app -from database import get_connection -from decorators import login_required, get_user_id, redis_conn -import env - - -@app.route("/send", methods=["POST"]) -@login_required -@get_user_id -def send_prog(user_id, uid): - connection = get_connection() - cur = connection.cursor() - - cur.execute("SELECT * FROM champs WHERE id = %s", (str(user_id),)) - - fetch = cur.fetchone() # Can be None - problems_ids_temp = fetch[3:] - problems_ids = [] - - sql = "SELECT * FROM problems WHERE id = -1 " - - strs = string.ascii_uppercase - - for i in problems_ids_temp: - if i is not None: - problems_ids.append(i) - sql += "OR id = %s " - - cur.execute(sql, tuple(problems_ids)) - - x = list(cur.fetchall()) - - problem_ = None - problem_letter_form = request.form["problem"] - - for i in x: - _id = i[0] - problem_letter = strs[problems_ids.index(_id)] - if problem_letter_form == problem_letter: - problem_ = i - - tests = problem_[5] - tests = json.loads(tests) - tests = list(map(lambda z: {"in": z[0], "out": z[1]}, tests)) - - print(problem_) - - f_lang = request.form["cars"] - f_code = request.form["src"] - - cur.execute( - f""" - INSERT INTO champSends_{user_id} - (problem_name, problem_id, user_id, send_time, state, program, problem_letter, lang) - VALUES(%s, %s, %s, %s, %s, %s, %s, %s); - """, - ( - problem_[1], - problem_[0], - uid, - datetime.datetime.now(), - "Тестируется", - f_code, - problem_letter_form, - f_lang, - ), - ) - cur.execute(f"SELECT currval(pg_get_serial_sequence('champSends_{user_id}','id'));") - - inserted_id = cur.fetchone()[0] - - meta = { - "champ_id": user_id, - "user_id": uid, - "problem": request.form["problem"], - "id": inserted_id, - } - - data = { - "meta": json.dumps(meta), - "source": request.form["src"], - "compiler": request.form["cars"], - "tests": tests, - } - - connection.commit() - - print(cur.fetchone()) - - cur.execute(f"SELECT address FROM servers WHERE id = %s", (request.form["cars"],)) - - server_addr = cur.fetchone()[0] - - print() - - requests.post( - f"http://{server_addr}:{env.CHECKER_PORT}/api/v1/test", json=data, timeout=1000 - ) - return redirect("/sends") - - -@app.route("/api/check_system_callback", methods=["POST"]) -@redis_conn -def check_system(r): - data = request.json - print(data) - all_count = 0 - correct_count = 0 - for results in data["results"]: - all_count += 1 - if results["success"]: - correct_count += 1 - - meta = json.loads(data["meta"]) - - print(round((correct_count / all_count) * 100)) - - champ_id = meta["champ_id"] - user_id = meta["user_id"] - column = meta["problem"][0] - - if not re.fullmatch("[0-9]+", str(champ_id)): - return "", 409 - if not re.fullmatch("[0-9]+", str(user_id)): - return "", 409 - if not re.fullmatch("[a-zA-Z]", str(column)): - return "", 409 - - con = get_connection() - cur = con.cursor() - - points = round((correct_count / all_count) * 100) - - cur.execute( - f"UPDATE champUsers_{champ_id} SET {column} = {points} \ - WHERE id= {user_id} AND ({column} < {points} OR {column} IS NULL)" - ) - - result_str = json.dumps(data["results"], indent=2) - - print(result_str) - - cur.execute( - f"UPDATE champSends_{champ_id} SET score = %s,state = 'Протестировано', description = %s WHERE id = %s;", - (round((correct_count / all_count) * 100), result_str, meta["id"]), - ) - - con.commit() - - r.delete(f"r-champ-{champ_id}-stats", f"r-champ-{champ_id}-sends-user-{user_id}") - - return "OK" diff --git a/BACKEND/src/web/solution_processing/quiz_api.py b/BACKEND/src/web/solution_processing/quiz_api.py deleted file mode 100644 index 8e53534..0000000 --- a/BACKEND/src/web/solution_processing/quiz_api.py +++ /dev/null @@ -1,110 +0,0 @@ -import datetime -import json -import re - -from flask import request -from psycopg2.extras import RealDictCursor - -from app import app -from database import get_connection -from decorators import login_required, get_user_id, redis_conn - - -@app.route("/api/send/quiz", methods=["POST"]) -@login_required -@get_user_id -@redis_conn -def send_quiz_solution(user_id, uid, r): - connection = get_connection() - cur = connection.cursor() - cur_dict = connection.cursor(cursor_factory=RealDictCursor) - - answers: dict - problem, answers = request.json["problem"], request.json["answers"] - - if not re.fullmatch("[a-zA-Z]", problem): - return "", 409 - - cur.execute(f"SELECT {problem.lower()} FROM champs WHERE id = %s", (str(user_id),)) - - fetch = cur.fetchone() # Can be None - problem_id = fetch[0] - print(problem_id) - - cur_dict.execute(f"SELECT tests, name FROM problems WHERE id = %s", (problem_id,)) - fetch = cur_dict.fetchone() - tests = fetch["tests"] - problem_name = fetch["name"] - - tests_dict = json.loads(tests) - questionById = {} - qustionsCount = 0 - for question in tests_dict["questions"]: - questionById[question["id"]] = question - qustionsCount += 1 - print(questionById) - - points = 0 - report = "" - for answer_id, answer in answers.items(): - current_answer = answer[0] - print(questionById) - correct_answer = questionById[int(answer_id)]["correct_answers"][0] - print(correct_answer) - report += "=" * 30 + "\n" - report += "Expected: " + "\n" - report += str(correct_answer) + "\n" - report += "Answered: " + "\n" - report += str(current_answer) + "\n" - - if current_answer == correct_answer: - points += 1 - report += f"Got 1 point" + "\n" - else: - report += f"Got 0 point" + "\n" - - report += "=" * 30 + "\n" - - totalPoints = int(points / qustionsCount * 100) - - report += "\n" - report += f"Final points: {points}/{qustionsCount} => {totalPoints}" - report += "\n" - - cur.execute( - f"""SELECT id FROM champSends_{user_id} where user_id={uid} and problem_id={problem_id}""" - ) - equals_sends = cur.fetchall() - print("!!!!!!", equals_sends) - - if len(equals_sends) == 0: - - cur.execute( - f""" - INSERT INTO champSends_{user_id} - (problem_name, problem_id, user_id, send_time, state, program, problem_letter, score) - VALUES(%s, %s, %s, %s, %s, %s, %s, %s); - """, - ( - problem_name, - problem_id, - uid, - datetime.datetime.now(), - "Подсчитано", - report, - problem, - totalPoints, - ), - ) - - cur.execute( - f""" - UPDATE champUsers_{user_id} SET {problem.lower()} = {totalPoints} - WHERE id= {uid}""" - ) - - connection.commit() - - r.delete(f"r-champ-{user_id}-stats", f"r-champ-{user_id}-sends-user-{uid}") - - return {"ok": "ok"} diff --git a/BACKEND/src/web/teacher_api/__init__.py b/BACKEND/src/web/teacher_api/__init__.py deleted file mode 100644 index c6638cb..0000000 --- a/BACKEND/src/web/teacher_api/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import web.teacher_api.auth -import web.teacher_api.champs -import web.teacher_api.students -import web.teacher_api.problems -import web.teacher_api.students_sends diff --git a/BACKEND/src/web/teacher_api/auth.py b/BACKEND/src/web/teacher_api/auth.py deleted file mode 100644 index 6d21b32..0000000 --- a/BACKEND/src/web/teacher_api/auth.py +++ /dev/null @@ -1,102 +0,0 @@ -from flask import request, make_response, jsonify -from psycopg2.extras import RealDictCursor - -import env -from app import app -from database import get_connection -from decorators import teacher_required -from services import captcha_service -from utils import salt_crypt - - -@app.route("/api/teacher/auth") -def teacher_auth_captcha(): - captcha = captcha_service.generate_with_validation() - return { - "base64string": captcha.base64image, - "validate": captcha.sha256_hash, - } - - -@app.route("/api/teacher/auth", methods=["POST"]) -def teacher_auth_post(): - login, password = request.json["login"], request.json["password"] - - if env.REQUIRE_CAPTCHA: - base64image, captchaUserInput, captchaValidate = ( - request.json["base64image"], - request.json["captchaUserInput"], - request.json["captchaValidate"], - ) - - success_captcha = captcha_service.validate( - base64image, captchaUserInput, captchaValidate - ) - - if not success_captcha: - return make_response({"success": False, "use_redirect": False}, 403) - - connection = get_connection() - cursor = connection.cursor(cursor_factory=RealDictCursor) - - cursor.execute( - f""" - SELECT id from globalusers - WHERE role = 'TEACHER' - AND password = %s - AND login = %s - """, - (password, login), - ) - - resp = make_response({"success": False, "use_redirect": False}, 403) - - if cursor.fetchone(): - resp = make_response({"success": True}) - client_hash = salt_crypt(login, password) - resp.set_cookie("teacher", f"{login}_{password}_{client_hash}") - - return resp - - -@app.route("/api/teacher/changecred", methods=["POST"]) -@teacher_required -def teacher_change_credential_post(): - new_login, new_password, current_password = ( - request.json["login"], - request.json["password"], - request.json["current_password"], - ) - - connection = get_connection() - cursor = connection.cursor(cursor_factory=RealDictCursor) - - cursor.execute( - f""" - SELECT id from globalusers - WHERE role = 'TEACHER' - AND password = %s - AND login = %s - """, - (current_password, new_login), - ) - - resp = make_response({"success": False, "use_redirect": False}, 422) - - user_db = cursor.fetchone() - - print(user_db) - if user_db is None: - pass - elif len(user_db) > 0: - cursor.execute( - "UPDATE globalusers SET login = %s, password = %s WHERE id = %s;", - (new_login, new_password, user_db["id"]), - ) - resp = make_response({"success": True}) - client_hash = salt_crypt(new_login, new_password) - resp.set_cookie("teacher", f"{new_login}_{new_password}_{client_hash}") - - connection.commit() - - return resp diff --git a/BACKEND/src/web/teacher_api/champs.py b/BACKEND/src/web/teacher_api/champs.py deleted file mode 100644 index 9795ed8..0000000 --- a/BACKEND/src/web/teacher_api/champs.py +++ /dev/null @@ -1,121 +0,0 @@ -import re -import string - -from flask import request, redirect - -from app import app -from database import get_connection -from database.createTables import get_query_users_table, get_query_sends_table -from decorators import teacher_required - - -@app.route("/api/teacher/champs") -@teacher_required -def get_champs_route(): - connection = get_connection() - cursor = connection.cursor() - - cursor.execute("SELECT id, name, started FROM champs ") - - champs = cursor.fetchall() - champs = list(map(lambda x: [*x], champs)) - champs = list(map(lambda x: {"id": x[0], "name": x[1], "start_dt": x[2]}, champs)) - - return champs - - -@app.route("/api/teacher/champs", methods=["POST"]) -@teacher_required -def create_champ_post_teacher(): - name = request.json["name"] - - connection = get_connection() - cur = connection.cursor() - - cur.execute("INSERT INTO champs (name) VALUES (%s)", (name,)) - - connection.commit() - - cur.execute("SELECT currval(pg_get_serial_sequence('champs','id'));") - champ_id = cur.fetchone()[0] - - cur.execute(get_query_users_table(champ_id)) - cur.execute(get_query_sends_table(champ_id)) - - connection.commit() - - return redirect("/admin") - - -@app.route("/api/teacher/champs/") -@teacher_required -# @ValidateParameters -def get_champs_byid_route(champ_id): - connection = get_connection() - cur = connection.cursor() - - cur.execute("SELECT * FROM champs WHERE id = %s", (str(champ_id),)) - - fetch = cur.fetchone() # Can be None - problems_ids_temp = fetch[3:] - problems_ids = [] - - sql = "SELECT * FROM problems WHERE id = -1 " - - strs = string.ascii_uppercase - - for task in problems_ids_temp: - if task is not None: - problems_ids.append(task) - sql += "OR id = %s " - - cur.execute(sql, tuple(problems_ids)) - - tasks_dict = dict.fromkeys(problems_ids) - - x = list(cur.fetchall()) - - tasks = [] - - for task in x: - _id = task[0] - name = task[1] - tasks_dict[_id] = task - tasks.append({"letter": strs[problems_ids.index(_id)], "id": _id, "name": name}) - - return {"tasks": tasks, "id": fetch[0], "name": fetch[1]} - - -@app.route("/api/teacher/champs/", methods=["POST"]) -@teacher_required -def settings_post_teacher_api(champ_id): - connection = get_connection() - cur = connection.cursor() - - form = request.json - problem = form["problem"] - problem_id = form["problem_id"] - - cur.execute(f"""SELECT id FROM problems WHERE id=%s""", (problem_id,)) - - prefetched_problem = cur.fetchone() - - print(prefetched_problem) - - if problem_id == "" or prefetched_problem is None: - return {"success": "false"}, 400 - problem_id = int(problem_id) - - print(problem, problem_id) - - problem_validation_result = re.fullmatch("[a-zA-z]", problem) - if not problem_validation_result: - return {"success": "false"}, 400 - - cur.execute( - f"""UPDATE champs SET {problem} = %s WHERE id = %s""", (problem_id, champ_id) - ) - - connection.commit() - - return {"success": "true"} diff --git a/BACKEND/src/web/teacher_api/problems.py b/BACKEND/src/web/teacher_api/problems.py deleted file mode 100644 index d253d92..0000000 --- a/BACKEND/src/web/teacher_api/problems.py +++ /dev/null @@ -1,111 +0,0 @@ -import json - -from flask import request -from psycopg2.extras import RealDictCursor - -from app import app -from database import get_connection -from decorators import teacher_required -from utils import fix_new_line - - -@app.route("/api/teacher/problems") -@teacher_required -def get_problems_api_route(): - connection = get_connection() - cursor = connection.cursor(cursor_factory=RealDictCursor) - - cursor.execute("SELECT id, name, description FROM problems") - problems = cursor.fetchall() - - return problems - - -@app.route("/api/teacher/problems/") -@teacher_required -def get_problems_byid_api_route(problem_id): - connection = get_connection() - cursor = connection.cursor(cursor_factory=RealDictCursor) - - cursor.execute( - f""" - SELECT id, name, description, "in", "out", examples, tests - FROM problems WHERE id = %s - """, - (problem_id,), - ) - problem = cursor.fetchone() - - print(problem) - - if problem is None: - return "", 404 - - out_data = { - "tests": [], - "examples": fix_new_line(json.loads(problem["examples"])), - "name": problem["name"], - "description": problem["description"], - "out_data": problem["out"], - "in_data": problem["in"], - } - - return out_data - - -@app.route("/api/teacher/problems/add", methods=["POST"]) -@teacher_required -def teacher_list_problems_add(): - connection = get_connection() - cursor = connection.cursor() - - build = request.json["build"] - print(build) - - try: - build_json = json.loads(build) - - problem_type = build_json.get("type", "question") - except Exception as e: - print("Maybe Json parse exception \n" + str(e)) - return "Not json (404 ERR)", 400 - - print() - - if problem_type == "quiz": - print() - - cursor.execute( - """ - INSERT INTO problems (name, description, "in", out, examples, tests, is_question) - VALUES (%s, %s, %s, %s, %s, %s, TRUE) - """, - (build_json["name"], "Тест", "-", "-", "[]", json.dumps(build_json)), - ) - - pass - elif problem_type == "question": - build_json["tests"] = json.dumps(build_json["tests"]) - build_json["examples"] = json.dumps(build_json["examples"]) - - print(build) - print(build_json) - - cursor.execute( - """ - INSERT INTO problems (name, description, "in", out, examples, tests) - VALUES (%s, %s, %s, %s, %s, %s) - """, - ( - build_json["name"], - build_json["description"], - build_json["in"], - build_json["out"], - build_json["examples"], - build_json["tests"], - ), - ) - - connection.commit() - - return {"success": True} diff --git a/BACKEND/src/web/teacher_api/students.py b/BACKEND/src/web/teacher_api/students.py deleted file mode 100644 index 752e977..0000000 --- a/BACKEND/src/web/teacher_api/students.py +++ /dev/null @@ -1,47 +0,0 @@ -import random - -from flask import request -from psycopg2.extras import RealDictCursor - -from app import app -from database import get_connection -from decorators import teacher_required - -from decorators import redis_conn - - -@app.route("/api/teacher/champs//users") -@teacher_required -def get_users_get_route(champ_id): - connection = get_connection() - cur = connection.cursor(cursor_factory=RealDictCursor) - - cur.execute(f"SELECT name, login, password FROM champUsers_{champ_id}") - users = cur.fetchall() - - return users - - -@app.route("/api/teacher/champs//add_users", methods=["POST"]) -@teacher_required -@redis_conn -def create_users_in_champ_post_teachers_api(champ_id, r): - users = request.json["users"].replace("\r", "").split("\n") - - connection = get_connection() - cursor = connection.cursor() - - for name in users: - login = "".join(map(str, [random.randint(0, 9) for _ in range(5)])) - password = "".join(map(str, [random.randint(0, 9) for _ in range(5)])) - - cursor.execute( - f"INSERT INTO champUsers_{champ_id} (login, password, name) VALUES (%s, %s, %s)", - (login, password, name), - ) - - connection.commit() - - r.delete(f"r-champ-{champ_id}-stats") - - return {"success": "true"}, 201 diff --git a/BACKEND/src/web/teacher_api/students_sends.py b/BACKEND/src/web/teacher_api/students_sends.py deleted file mode 100644 index a167c0e..0000000 --- a/BACKEND/src/web/teacher_api/students_sends.py +++ /dev/null @@ -1,138 +0,0 @@ -import json -import string - -import psycopg2 -from flask import request, abort -from psycopg2.extras import RealDictCursor - -from app import app -from database import get_connection -from decorators import redis_conn -from web.api.battle import JSON_MIMETYPE - - -@app.route("/api/teacher/champs//stats") -@redis_conn -def get_stats_teacher_api(champ_id, r): - redis_cache = r.get(f"r-champ-{champ_id}-stats") - if redis_cache is not None: - response = app.response_class( - response=redis_cache, - status=200, - mimetype=JSON_MIMETYPE, - ) - print("redis") - return response - - connection = get_connection() - cur = connection.cursor() - - cur.execute("SELECT * FROM champs WHERE id = %s", (str(champ_id),)) - - fetch = cur.fetchone() # Can be None - problems_ids_temp = fetch[3:] - problems_counts = 0 - - for i in problems_ids_temp: - if i is not None: - problems_counts += 1 - else: - break - - strs = string.ascii_uppercase - - print(problems_counts) - - cur.execute( - f""" - SELECT u.*, MAX(s.send_time) AS send_time - FROM champusers_{champ_id} u - LEFT JOIN champsends_{champ_id} s ON u.id = s.user_id - GROUP BY u.id - ORDER BY score DESC, send_time ASC; - """ - ) - - fetch = cur.fetchall() # Can be None - - users = [] - - for i, usr in enumerate(fetch): - user_id = usr[0] - score = usr[15] - last_send = usr[16] - last_send = ( - None if last_send is None else last_send.strftime("%m/%d/%Y, %H:%M:%S") - ) - - nickname = usr[3] - problems_score = usr[4 : problems_counts + 4] - - # problems_score = list(map(lambda s: (s, "")[s is None], problems_score)) - - users.append( - { - "position": i + 1, - "name": nickname, - "user_id": user_id, - "score": score, - "problems_score": problems_score, - "last_send": last_send, - } - ) - - print() - - resp_string = {"success": True, "cols": strs[:problems_counts], "users": users} - - r.set(f"r-champ-{champ_id}-stats", json.dumps(resp_string)) - return resp_string - - -@app.route("/api/teacher/champs//stats/search") -def get_stats_teacher_api_get_by_task_and_user(champ_id): - args = request.args - user_id, problem_letter = args["user_id"], args["problem"] - - conn = get_connection() - cursor = conn.cursor(cursor_factory=RealDictCursor) - - try: - cursor.execute( - f""" - SELECT * - FROM champsends_{champ_id} - WHERE problem_letter = %s - AND user_id = %s - ORDER BY score DESC, send_time DESC - LIMIT 1 - - """, - (problem_letter, user_id), - ) - except psycopg2.errors.UndefinedTable: - abort(404) - - res = cursor.fetchone() - - if res is None: - abort(404) - - tests = [] - result = [] - try: - result = json.loads(res["description"]) - except TypeError: - pass - - for i, test in enumerate(result): - message = test["msg"] - out = test["out"] - - if message == "WRONG_ANSWER": - out = """ВЫВОД СКРЫТ""" - - to_add = {"id": i + 1, "time": test["time"], "msg": message, "out": out} - tests.append(to_add) - - return {**{"tests": tests}, **res} diff --git a/BACKEND/src/web/validation_form/__init__.py b/BACKEND/src/web/validation_form/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/BACKEND/src/web/validation_form/admin.py b/BACKEND/src/web/validation_form/admin.py deleted file mode 100644 index e69de29..0000000 diff --git a/BACKEND/src/web/validation_form/api.py b/BACKEND/src/web/validation_form/api.py deleted file mode 100644 index 24f5ffb..0000000 --- a/BACKEND/src/web/validation_form/api.py +++ /dev/null @@ -1,16 +0,0 @@ -from wtforms import Form, StringField, IntegerField -from wtforms.validators import DataRequired, Regexp - -from utils import LETTER_REGEX - - -class LoginForm(Form): - id = IntegerField("id", validators=[DataRequired()]) - login = StringField("login", validators=[DataRequired()]) - password = StringField("password", validators=[DataRequired()]) - - -class SendProgramForm(Form): - cars = StringField("cars", validators=[DataRequired()]) - src = StringField("src", validators=[DataRequired()]) - problem = StringField("problem", validators=[DataRequired(), Regexp(LETTER_REGEX)]) diff --git a/BACKEND/src/web/validation_form/solution_processing.py b/BACKEND/src/web/validation_form/solution_processing.py deleted file mode 100644 index e69de29..0000000 diff --git a/BACKEND/src/web/validation_form/teacher_api.py b/BACKEND/src/web/validation_form/teacher_api.py deleted file mode 100644 index e69de29..0000000 diff --git a/BACKEND/src/wsgi.py b/BACKEND/src/wsgi.py deleted file mode 100644 index 033109f..0000000 --- a/BACKEND/src/wsgi.py +++ /dev/null @@ -1,3 +0,0 @@ -from main import webapp - -app = webapp() From 10643651d8cfa5f65be9f18a20da164d37b803af Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Tue, 20 May 2025 16:25:22 +0300 Subject: [PATCH 092/117] Remove old draft service --- REPORTER_WORKER/.env.example | 7 - REPORTER_WORKER/.gitignore | 162 --------- REPORTER_WORKER/app.py | 13 - REPORTER_WORKER/csv_export_from_db.py | 34 -- REPORTER_WORKER/filename.csv | 71 ---- REPORTER_WORKER/generate_report.py | 15 - REPORTER_WORKER/jwt_gen.py | 18 - .../localstorage/example/20241011_115432.pdf | Bin 19264 -> 0 bytes .../localstorage/example/filename.csv | 71 ---- .../localstorage/example/gen_report.py | 327 ------------------ REPORTER_WORKER/pipelines/gen_report.py | 327 ------------------ REPORTER_WORKER/requirements.txt | 8 - REPORTER_WORKER/web/controller/__init__.py | 2 - REPORTER_WORKER/web/controller/blueprint.py | 4 - .../web/controller/externalController.py | 29 -- .../web/controller/internalController.py | 48 --- .../web/service/dataBaseService.py | 41 --- .../web/service/databaseExportService.py | 41 --- REPORTER_WORKER/web/service/envService.py | 18 - REPORTER_WORKER/web/service/jwtService.py | 5 - 20 files changed, 1241 deletions(-) delete mode 100644 REPORTER_WORKER/.env.example delete mode 100644 REPORTER_WORKER/.gitignore delete mode 100644 REPORTER_WORKER/app.py delete mode 100644 REPORTER_WORKER/csv_export_from_db.py delete mode 100644 REPORTER_WORKER/filename.csv delete mode 100644 REPORTER_WORKER/generate_report.py delete mode 100644 REPORTER_WORKER/jwt_gen.py delete mode 100644 REPORTER_WORKER/localstorage/example/20241011_115432.pdf delete mode 100644 REPORTER_WORKER/localstorage/example/filename.csv delete mode 100644 REPORTER_WORKER/localstorage/example/gen_report.py delete mode 100644 REPORTER_WORKER/pipelines/gen_report.py delete mode 100644 REPORTER_WORKER/requirements.txt delete mode 100644 REPORTER_WORKER/web/controller/__init__.py delete mode 100644 REPORTER_WORKER/web/controller/blueprint.py delete mode 100644 REPORTER_WORKER/web/controller/externalController.py delete mode 100644 REPORTER_WORKER/web/controller/internalController.py delete mode 100644 REPORTER_WORKER/web/service/dataBaseService.py delete mode 100644 REPORTER_WORKER/web/service/databaseExportService.py delete mode 100644 REPORTER_WORKER/web/service/envService.py delete mode 100644 REPORTER_WORKER/web/service/jwtService.py diff --git a/REPORTER_WORKER/.env.example b/REPORTER_WORKER/.env.example deleted file mode 100644 index 0504742..0000000 --- a/REPORTER_WORKER/.env.example +++ /dev/null @@ -1,7 +0,0 @@ -JWT_SECRET=jwtsecret -API_SECRET=internalapiprotection -PUBLIC_ENDPOINTS_PATH=storage -INTERNAL_ENDPOINTS_PATH=internal -DATABASE_NAME=cb -DATABASE_USER=postgres -DATABASE_PASSWORD=admin \ No newline at end of file diff --git a/REPORTER_WORKER/.gitignore b/REPORTER_WORKER/.gitignore deleted file mode 100644 index efa407c..0000000 --- a/REPORTER_WORKER/.gitignore +++ /dev/null @@ -1,162 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file diff --git a/REPORTER_WORKER/app.py b/REPORTER_WORKER/app.py deleted file mode 100644 index 0b76e05..0000000 --- a/REPORTER_WORKER/app.py +++ /dev/null @@ -1,13 +0,0 @@ -from flask import Flask - -from web.controller import * -from web.controller.blueprint import internal_api -from web.service.envService import envService - -app = Flask(__name__) - -app.register_blueprint(internal_api, url_prefix=f'/{envService.INTERNAL_ENDPOINTS_PATH}') -app.register_blueprint(external_api, url_prefix=f'/{envService.PUBLIC_ENDPOINTS_PATH}') - -if __name__ == '__main__': - app.run(debug=True) diff --git a/REPORTER_WORKER/csv_export_from_db.py b/REPORTER_WORKER/csv_export_from_db.py deleted file mode 100644 index b4f9dfc..0000000 --- a/REPORTER_WORKER/csv_export_from_db.py +++ /dev/null @@ -1,34 +0,0 @@ -# ============================================== -# 1. Подключаем библиотеки Python -# ============================================== -import psycopg2 -import csv - -# ============================================== -# 2. Подключаемся к базе данных PGSQL -# ============================================== -conn = psycopg2.connect(dbname='cb', user='postgres', - password='admin', host='localhost') -# ============================================== -# 3. Получаем данные, кладем их в курсор -# ============================================== -cursor = conn.cursor() -cursor.execute('select * from champsends_10') -# --- Получаем наименования колонок -column_names = [] -for row in cursor.description: - column_names.append(row[0]) -# ============================================== -# 4. Пишем файл CSV с колонками -# ============================================== -with open('filename.csv', 'w', newline='', encoding="utf-8") as filename: - write_filename = csv.writer(filename, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) - write_filename.writerow(column_names) - for row in cursor: - write_filename.writerow(row) -# ============================================== -# 5. Закрываем курсор -# Закрываем соединение с Базой данных -# ============================================== -cursor.close() -conn.close() \ No newline at end of file diff --git a/REPORTER_WORKER/filename.csv b/REPORTER_WORKER/filename.csv deleted file mode 100644 index 39fcfc4..0000000 --- a/REPORTER_WORKER/filename.csv +++ /dev/null @@ -1,71 +0,0 @@ -id,problem_letter,problem_name,problem_id,user_id,send_time,state,description,program,score,lang -22,A,Neon,2,2,2023-09-24 18:47:50.208808,Тестируется,,sq,100,2 -18,C,Neon,2,2,2023-09-21 15:00:15.473756,Тестируется,,python,90,2 -25,A,Neon,2,2,2023-09-24 18:51:36.057261,Тестируется,,sq,,2 -26,A,Neon,2,2,2023-10-15 13:13:31.559252,Тестируется,,"import time - -time.sleep(5000)",,1 -28,A,Neon,2,2,2023-10-18 18:27:19.409318,Тестируется,,"print(""478"")",,1 -19,C,Neon,2,2,2023-09-21 15:01:00.288296,Тестируется,,python,50,2 -21,C,Neon,2,2,2023-09-21 15:01:53.896737,Тестируется,,python,5,2 -30,B,Футбол,5,2,2023-10-26 13:08:40.708081,Тестируется,"[ - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\4/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 22 - }, - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\5/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 12 - }, - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\6/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 10 - } -]",sq,,1 -2,B,Neon,2,1,2023-09-24 18:51:05.416562,Протестировано,"[ - { - ""success"": false, - ""out"": ""Main.java:1: error: reached end of file while parsing\nsq\n^\n1 error\nerror: compilation failed"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 326 - }, - { - ""success"": false, - ""out"": ""Main.java:1: error: reached end of file while parsing\nsq\n^\n1 error\nerror: compilation failed"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 287 - } -]",sq,0,2 -31,B,Футбол,5,2,2023-11-10 17:02:11.970514,Протестировано,"[ - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\4/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 22 - }, - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\5/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 12 - }, - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\6/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 10 - } -]",05,100,1 -17,C,Neon,2,4,2023-09-21 14:59:32.451938,Тестируется,,python,0,2 -20,C,Neon,2,4,2023-09-21 15:01:27.323250,Тестируется,,python,80,2 -23,A,Neon,2,3,2023-09-24 18:50:03.296020,Тестируется,,sq,,2 -27,A,Neon,2,3,2023-10-15 13:13:33.810387,Тестируется,,"import time - -time.sleep(5000)",,1 -29,A,Neon,2,4,2023-10-26 13:07:50.535370,Тестируется,,x = 0,,1 diff --git a/REPORTER_WORKER/generate_report.py b/REPORTER_WORKER/generate_report.py deleted file mode 100644 index ae8f563..0000000 --- a/REPORTER_WORKER/generate_report.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -import shutil - - -path = "localstorage/2" - -os.mkdir(path) -shutil.copyfile("pipelines/gen_report.py", f"{path}/gen_report.py") -shutil.copyfile("filename.csv", f"{path}/filename.csv") - -os.chdir("localstorage/2") - -with open("gen_report.py", encoding="utf-8") as hello: - exec(hello.read()) - diff --git a/REPORTER_WORKER/jwt_gen.py b/REPORTER_WORKER/jwt_gen.py deleted file mode 100644 index 32af38d..0000000 --- a/REPORTER_WORKER/jwt_gen.py +++ /dev/null @@ -1,18 +0,0 @@ -import jwt - -import time - -# Python3 code to illustrate the addition -# of time onto the datetime object - -# Importing datetime -import datetime - -# Initializing a date and time -date_and_time = datetime.datetime.now() -# Calling the timedelta() function -time_change = datetime.timedelta(minutes=1) -new_time = date_and_time + time_change - -encoded = jwt.encode({"reportID": "1", "filename": "background.jpg", "valid": new_time.timestamp()}, "secret", algorithm="HS256") -print(encoded) diff --git a/REPORTER_WORKER/localstorage/example/20241011_115432.pdf b/REPORTER_WORKER/localstorage/example/20241011_115432.pdf deleted file mode 100644 index 8bb96259ef2ac55e407848cc331691ef22c90813..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19264 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 o02taDZ4@z94giqddI3DNGtLx?+j4=x(P-c\n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 22 - }, - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\5/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 12 - }, - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\6/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 10 - } -]",sq,,1 -2,B,Neon,2,1,2023-09-24 18:51:05.416562,Протестировано,"[ - { - ""success"": false, - ""out"": ""Main.java:1: error: reached end of file while parsing\nsq\n^\n1 error\nerror: compilation failed"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 326 - }, - { - ""success"": false, - ""out"": ""Main.java:1: error: reached end of file while parsing\nsq\n^\n1 error\nerror: compilation failed"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 287 - } -]",sq,0,2 -31,B,Футбол,5,2,2023-11-10 17:02:11.970514,Протестировано,"[ - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\4/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 22 - }, - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\5/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 12 - }, - { - ""success"": false, - ""out"": ""Traceback (most recent call last):\n File \""/adir\\6/main.py\"", line 1, in \n xxxx\nNameError: name 'xxxx' is not defined"", - ""msg"": ""RUNTIME_ERROR"", - ""time"": 10 - } -]",05,100,1 -17,C,Neon,2,4,2023-09-21 14:59:32.451938,Тестируется,,python,0,2 -20,C,Neon,2,4,2023-09-21 15:01:27.323250,Тестируется,,python,80,2 -23,A,Neon,2,3,2023-09-24 18:50:03.296020,Тестируется,,sq,,2 -27,A,Neon,2,3,2023-10-15 13:13:33.810387,Тестируется,,"import time - -time.sleep(5000)",,1 -29,A,Neon,2,4,2023-10-26 13:07:50.535370,Тестируется,,x = 0,,1 diff --git a/REPORTER_WORKER/localstorage/example/gen_report.py b/REPORTER_WORKER/localstorage/example/gen_report.py deleted file mode 100644 index d074904..0000000 --- a/REPORTER_WORKER/localstorage/example/gen_report.py +++ /dev/null @@ -1,327 +0,0 @@ -# -*- coding: utf-8 -*- -"""Codebattles.ipynb - -Automatically generated by Colab. - -Original file is located at - https://colab.research.google.com/drive/1zgmmvq-JoBJlsAtONzosZEQ8a334dloP -""" - -import pandas as pd -from matplotlib import pyplot as plt -import numpy as np - -all_problems = ("A","B","C", "D","E","F") - -def merge(ser): - for letter in all_problems: - if letter not in ser.index: - ser[letter] = 0 - -df = pd.read_csv("filename.csv") - -df - -xdf = df.dropna(subset=['score']) -xdf - -# df = pd.read_csv("/content/generated_cv.csv") - -df = pd.DataFrame() -df["Буква задачи"] = xdf["problem_letter"] -df["Результат в баллах"] = xdf["score"] -df["Имя"] = xdf["id"] -df["Время отправки"] = xdf["send_time"] - -df - -import matplotlib.pyplot as plt - - - -def graph1(fig,ax,callback=lambda : True): - task_counts = df['Буква задачи'].value_counts() - - # Сортировка по алфавиту - task_counts_sorted = task_counts.sort_index() - - merge(task_counts_sorted) - - print(task_counts_sorted) - - # Построение графика с отсортированной осью X - plt.figure(figsize=(10, 5)) - task_counts_sorted.plot(kind='bar', color='skyblue', ax=ax) - - ax.set_ylabel('Решения') - ax.set_title('Количество решений по задачам (сортировка по алфавиту)') - - - # Настройка графика - plt.title('Количество решений по задачам (сортировка по алфавиту)') - - plt.ylabel('Количество решений') - plt.xticks(rotation=0) - plt.grid(axis='y') - - callback() - plt.close() - - - -# figure = plt.figure(figsize=(7, 10)) -figure = plt.figure(figsize=(10, 6)) -axes = figure.subplots(1, 1) -# axes = figure.subplots(2, 1) -graph1(figure,axes) -# graph1(figure,axes[0]) -# graph1(figure,axes[1]) -plt.show() - -import matplotlib.pyplot as plt - -def graph2(fig,ax,callback=lambda : True): - fig = plt.figure() - # Подсчет количества решений по каждой задаче - task_counts = df['Буква задачи'].value_counts() - - merge(task_counts) - - # Сортировка по алфавиту - task_counts_sorted = task_counts.sort_index() - - - - # Построение графика с отсортированной осью X - plt.figure(figsize=(10, 6)) - task_counts.plot(kind='bar', color='green',ax=ax) - - # Настройка графика - plt.title('Количество решений по задачам (сортировка по количеству)') - plt.xlabel('Буква задачи') - plt.ylabel('Количество решений') - plt.xticks(rotation=0) - plt.grid(axis='y') - - - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - # plt.close() - -# graph2(plt.show) - -figure = plt.figure(figsize=(7, 10)) -# figure = plt.figure(figsize=(10, 6)) -# axes = figure.subplots(1, 1) -axes = figure.subplots(2, 2) -# graph1(figure,axes) -graph2(figure,axes[0,0]) -graph2(figure,axes[0,1]) -graph2(figure,axes[1,0]) -graph2(figure,axes[1,1]) -plt.show() - -def graph3(callback=lambda : True): - fig = plt.figure() - # Подсчет количества верных решений (>= 50 баллов) для каждой задачи - df['Верное решение'] = df['Результат в баллах'] >= 50 - correct_solutions_percentage = df.groupby('Буква задачи')['Верное решение'].mean() * 100 - - # Сортировка по алфавиту - correct_solutions_sorted = correct_solutions_percentage.sort_index() - merge(correct_solutions_sorted) - - # Построение графика с процентом верных решений - plt.figure(figsize=(10, 6)) - correct_solutions_sorted.plot(kind='bar', color='lightgreen') - - print(correct_solutions_sorted) - - - # Настройка графика - plt.title('Процент верных решений по задачам (>= 50 баллов)') - plt.xlabel('Буква задачи') - plt.ylabel('Процент верных решений (%)') - plt.xticks(rotation=0) - plt.grid(axis='y') - - # Показ графика - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - plt.close() - -graph3(plt.show) - -def graph3_1(callback=lambda : True): - fig = plt.figure() - # Подсчет количества верных решений (>= 50 баллов) для каждой задачи - df['Верное решение'] = df['Результат в баллах'] >= 50 - correct_solutions_count = df.groupby('Буква задачи')['Верное решение'].sum() - - # Сортировка по алфавиту - correct_solutions_sorted = correct_solutions_count.sort_index() - merge(correct_solutions_sorted) - - # Построение графика с процентом верных решений - plt.figure(figsize=(10, 6)) - correct_solutions_sorted.plot(kind='bar', color='lightgreen') - - print(correct_solutions_sorted) - - - # Настройка графика - plt.title('Количество верных решений (>= 50 баллов)') - plt.xlabel('Буква задачи') - plt.ylabel('Количество человек, решивших задчу') - plt.xticks(rotation=0) - plt.grid(axis='y') - - # Показ графика - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - plt.close() - -graph3_1(plt.show) - -def graph4(callback=lambda : True): - import matplotlib.pyplot as plt - - correct_solutions = df[df["Результат в баллах"] >= 50].shape[0] - incorrect_solutions = df[df["Результат в баллах"] < 50].shape[0] - # Данные: количество правильных и неправильных решений - # correct_solutions = 10 # например, 70 правильных решений - # incorrect_solutions = 30 # например, 30 неправильных решений - - # Создаем список данных для диаграммы - data = [correct_solutions, incorrect_solutions] - labels = ['Правильные решения', 'Неправильные решения'] - colors = ['#4CAF50', '#F44336'] # зеленый для правильных, красный для неправильных - - # Построение круговой диаграммы - plt.figure(figsize=(10,6)) - plt.pie(data, labels=labels, autopct='%1.1f%%', startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}) - - # Настройка диаграммы - plt.title('Процент правильных и неправильных решений') - # plt.axis('equal') # чтобы круг был кругом, а не овалом - - # Показ диаграммы - # Показ графика - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - plt.close() - -graph4(plt.show) - -df['Время отправки'] = pd.to_datetime(df['Время отправки']) - -import matplotlib.pyplot as plt - -def titlegen(callback=lambda : True): - import matplotlib.pyplot as plt - - fig, ax = plt.subplots(figsize=(10, 6)) - - # Убираем оси - ax.axis('off') - - champ_name = "10Г урок 13.09" - - # Добавляем текст на график - ax.text(0.5, 0.7, 'Codebattles отчет', fontsize=25, ha='center', va='center') - ax.text(0.5, 0.5, f'Соревнование: {champ_name}', fontsize=18, ha='center', va='center') - - ax.text(0.5, 0.4, f'Начало: 13.09.2008 15:00:00', fontsize=18, ha='center', va='center') - ax.text(0.5, 0, f'Отчет сформирован: 13.09.2008 15:00:00', fontsize=18, ha='center', va='center') - - # Показ диаграммы - # Показ графика - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - plt.close() - -titlegen(plt.show) - -# Группировка по времени отправок с точностью до часа - -df_grouped = df.groupby(df['Время отправки'].dt.floor('h')).size() - -# Построение графика -plt.figure(figsize=(10,6)) -plt.plot(df_grouped.index, df_grouped.values, marker='o', linestyle='-', color='b') - -# Настройки графика -plt.title('Количество отправок по времени') -plt.xlabel('Время отправки (с точностью до часа)') -plt.ylabel('Количество отправок') -plt.xticks(rotation=45) # Повернуть метки оси X для лучшей читаемости -plt.grid(True) - -# Показ графика -plt.tight_layout() -plt.show() - -import datetime -import matplotlib.pyplot as plt -from matplotlib.backends.backend_pdf import PdfPages - -# Получение текущей даты и времени -timestamp = datetime.datetime.now() - -# Форматирование таймстемпа без пробелов -timestamp_str = timestamp.strftime("%Y%m%d_%H%M%S") - -with PdfPages(f'{timestamp_str}.pdf') as pdf: - # titlegen(pdf.savefig) - # graph1(pdf.savefig) - # graph2(pdf.savefig) - # graph3(pdf.savefig) - # graph3_1(pdf.savefig) - # graph4(pdf.savefig) - - figure = plt.figure(figsize=(8, 12)) - # figure = plt.figure(figsize=(10, 6)) - # axes = figure.subplots(1, 1) - axes = figure.subplots(2, 2) - # graph1(figure,axes) - graph2(figure,axes[0,0]) - graph2(figure,axes[0,1]) - graph2(figure,axes[1,0]) - graph2(figure,axes[1,1]) - pdf.savefig(figure) - plt.close() - - figure2 = plt.figure(figsize=(8, 12)) - # figure = plt.figure(figsize=(10, 6)) - # axes = figure.subplots(1, 1) - axes = figure2.subplots(2, 1) - # graph1(figure,axes) - graph1(figure2,axes[0]) - graph1(figure2,axes[1]) - # graph1(figure,axes[1,0]) - # graph1(figure,axes[1,1]) - pdf.savefig(figure2) - plt.close() - - # figure.close() - # plt.show() \ No newline at end of file diff --git a/REPORTER_WORKER/pipelines/gen_report.py b/REPORTER_WORKER/pipelines/gen_report.py deleted file mode 100644 index d074904..0000000 --- a/REPORTER_WORKER/pipelines/gen_report.py +++ /dev/null @@ -1,327 +0,0 @@ -# -*- coding: utf-8 -*- -"""Codebattles.ipynb - -Automatically generated by Colab. - -Original file is located at - https://colab.research.google.com/drive/1zgmmvq-JoBJlsAtONzosZEQ8a334dloP -""" - -import pandas as pd -from matplotlib import pyplot as plt -import numpy as np - -all_problems = ("A","B","C", "D","E","F") - -def merge(ser): - for letter in all_problems: - if letter not in ser.index: - ser[letter] = 0 - -df = pd.read_csv("filename.csv") - -df - -xdf = df.dropna(subset=['score']) -xdf - -# df = pd.read_csv("/content/generated_cv.csv") - -df = pd.DataFrame() -df["Буква задачи"] = xdf["problem_letter"] -df["Результат в баллах"] = xdf["score"] -df["Имя"] = xdf["id"] -df["Время отправки"] = xdf["send_time"] - -df - -import matplotlib.pyplot as plt - - - -def graph1(fig,ax,callback=lambda : True): - task_counts = df['Буква задачи'].value_counts() - - # Сортировка по алфавиту - task_counts_sorted = task_counts.sort_index() - - merge(task_counts_sorted) - - print(task_counts_sorted) - - # Построение графика с отсортированной осью X - plt.figure(figsize=(10, 5)) - task_counts_sorted.plot(kind='bar', color='skyblue', ax=ax) - - ax.set_ylabel('Решения') - ax.set_title('Количество решений по задачам (сортировка по алфавиту)') - - - # Настройка графика - plt.title('Количество решений по задачам (сортировка по алфавиту)') - - plt.ylabel('Количество решений') - plt.xticks(rotation=0) - plt.grid(axis='y') - - callback() - plt.close() - - - -# figure = plt.figure(figsize=(7, 10)) -figure = plt.figure(figsize=(10, 6)) -axes = figure.subplots(1, 1) -# axes = figure.subplots(2, 1) -graph1(figure,axes) -# graph1(figure,axes[0]) -# graph1(figure,axes[1]) -plt.show() - -import matplotlib.pyplot as plt - -def graph2(fig,ax,callback=lambda : True): - fig = plt.figure() - # Подсчет количества решений по каждой задаче - task_counts = df['Буква задачи'].value_counts() - - merge(task_counts) - - # Сортировка по алфавиту - task_counts_sorted = task_counts.sort_index() - - - - # Построение графика с отсортированной осью X - plt.figure(figsize=(10, 6)) - task_counts.plot(kind='bar', color='green',ax=ax) - - # Настройка графика - plt.title('Количество решений по задачам (сортировка по количеству)') - plt.xlabel('Буква задачи') - plt.ylabel('Количество решений') - plt.xticks(rotation=0) - plt.grid(axis='y') - - - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - # plt.close() - -# graph2(plt.show) - -figure = plt.figure(figsize=(7, 10)) -# figure = plt.figure(figsize=(10, 6)) -# axes = figure.subplots(1, 1) -axes = figure.subplots(2, 2) -# graph1(figure,axes) -graph2(figure,axes[0,0]) -graph2(figure,axes[0,1]) -graph2(figure,axes[1,0]) -graph2(figure,axes[1,1]) -plt.show() - -def graph3(callback=lambda : True): - fig = plt.figure() - # Подсчет количества верных решений (>= 50 баллов) для каждой задачи - df['Верное решение'] = df['Результат в баллах'] >= 50 - correct_solutions_percentage = df.groupby('Буква задачи')['Верное решение'].mean() * 100 - - # Сортировка по алфавиту - correct_solutions_sorted = correct_solutions_percentage.sort_index() - merge(correct_solutions_sorted) - - # Построение графика с процентом верных решений - plt.figure(figsize=(10, 6)) - correct_solutions_sorted.plot(kind='bar', color='lightgreen') - - print(correct_solutions_sorted) - - - # Настройка графика - plt.title('Процент верных решений по задачам (>= 50 баллов)') - plt.xlabel('Буква задачи') - plt.ylabel('Процент верных решений (%)') - plt.xticks(rotation=0) - plt.grid(axis='y') - - # Показ графика - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - plt.close() - -graph3(plt.show) - -def graph3_1(callback=lambda : True): - fig = plt.figure() - # Подсчет количества верных решений (>= 50 баллов) для каждой задачи - df['Верное решение'] = df['Результат в баллах'] >= 50 - correct_solutions_count = df.groupby('Буква задачи')['Верное решение'].sum() - - # Сортировка по алфавиту - correct_solutions_sorted = correct_solutions_count.sort_index() - merge(correct_solutions_sorted) - - # Построение графика с процентом верных решений - plt.figure(figsize=(10, 6)) - correct_solutions_sorted.plot(kind='bar', color='lightgreen') - - print(correct_solutions_sorted) - - - # Настройка графика - plt.title('Количество верных решений (>= 50 баллов)') - plt.xlabel('Буква задачи') - plt.ylabel('Количество человек, решивших задчу') - plt.xticks(rotation=0) - plt.grid(axis='y') - - # Показ графика - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - plt.close() - -graph3_1(plt.show) - -def graph4(callback=lambda : True): - import matplotlib.pyplot as plt - - correct_solutions = df[df["Результат в баллах"] >= 50].shape[0] - incorrect_solutions = df[df["Результат в баллах"] < 50].shape[0] - # Данные: количество правильных и неправильных решений - # correct_solutions = 10 # например, 70 правильных решений - # incorrect_solutions = 30 # например, 30 неправильных решений - - # Создаем список данных для диаграммы - data = [correct_solutions, incorrect_solutions] - labels = ['Правильные решения', 'Неправильные решения'] - colors = ['#4CAF50', '#F44336'] # зеленый для правильных, красный для неправильных - - # Построение круговой диаграммы - plt.figure(figsize=(10,6)) - plt.pie(data, labels=labels, autopct='%1.1f%%', startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}) - - # Настройка диаграммы - plt.title('Процент правильных и неправильных решений') - # plt.axis('equal') # чтобы круг был кругом, а не овалом - - # Показ диаграммы - # Показ графика - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - plt.close() - -graph4(plt.show) - -df['Время отправки'] = pd.to_datetime(df['Время отправки']) - -import matplotlib.pyplot as plt - -def titlegen(callback=lambda : True): - import matplotlib.pyplot as plt - - fig, ax = plt.subplots(figsize=(10, 6)) - - # Убираем оси - ax.axis('off') - - champ_name = "10Г урок 13.09" - - # Добавляем текст на график - ax.text(0.5, 0.7, 'Codebattles отчет', fontsize=25, ha='center', va='center') - ax.text(0.5, 0.5, f'Соревнование: {champ_name}', fontsize=18, ha='center', va='center') - - ax.text(0.5, 0.4, f'Начало: 13.09.2008 15:00:00', fontsize=18, ha='center', va='center') - ax.text(0.5, 0, f'Отчет сформирован: 13.09.2008 15:00:00', fontsize=18, ha='center', va='center') - - # Показ диаграммы - # Показ графика - callback() - - # Показ графика - #plt.show() - # plt.close() - #return fig - plt.close() - -titlegen(plt.show) - -# Группировка по времени отправок с точностью до часа - -df_grouped = df.groupby(df['Время отправки'].dt.floor('h')).size() - -# Построение графика -plt.figure(figsize=(10,6)) -plt.plot(df_grouped.index, df_grouped.values, marker='o', linestyle='-', color='b') - -# Настройки графика -plt.title('Количество отправок по времени') -plt.xlabel('Время отправки (с точностью до часа)') -plt.ylabel('Количество отправок') -plt.xticks(rotation=45) # Повернуть метки оси X для лучшей читаемости -plt.grid(True) - -# Показ графика -plt.tight_layout() -plt.show() - -import datetime -import matplotlib.pyplot as plt -from matplotlib.backends.backend_pdf import PdfPages - -# Получение текущей даты и времени -timestamp = datetime.datetime.now() - -# Форматирование таймстемпа без пробелов -timestamp_str = timestamp.strftime("%Y%m%d_%H%M%S") - -with PdfPages(f'{timestamp_str}.pdf') as pdf: - # titlegen(pdf.savefig) - # graph1(pdf.savefig) - # graph2(pdf.savefig) - # graph3(pdf.savefig) - # graph3_1(pdf.savefig) - # graph4(pdf.savefig) - - figure = plt.figure(figsize=(8, 12)) - # figure = plt.figure(figsize=(10, 6)) - # axes = figure.subplots(1, 1) - axes = figure.subplots(2, 2) - # graph1(figure,axes) - graph2(figure,axes[0,0]) - graph2(figure,axes[0,1]) - graph2(figure,axes[1,0]) - graph2(figure,axes[1,1]) - pdf.savefig(figure) - plt.close() - - figure2 = plt.figure(figsize=(8, 12)) - # figure = plt.figure(figsize=(10, 6)) - # axes = figure.subplots(1, 1) - axes = figure2.subplots(2, 1) - # graph1(figure,axes) - graph1(figure2,axes[0]) - graph1(figure2,axes[1]) - # graph1(figure,axes[1,0]) - # graph1(figure,axes[1,1]) - pdf.savefig(figure2) - plt.close() - - # figure.close() - # plt.show() \ No newline at end of file diff --git a/REPORTER_WORKER/requirements.txt b/REPORTER_WORKER/requirements.txt deleted file mode 100644 index 4a58c05..0000000 --- a/REPORTER_WORKER/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -PyJWT~=2.9.0 -Flask~=3.0.3 -Werkzeug~=3.0.4 -psycopg2~=2.9.9 -pandas~=2.2.3 -matplotlib~=3.9.2 -numpy~=2.1.2 -python-dotenv~=1.0.1 \ No newline at end of file diff --git a/REPORTER_WORKER/web/controller/__init__.py b/REPORTER_WORKER/web/controller/__init__.py deleted file mode 100644 index d6ff139..0000000 --- a/REPORTER_WORKER/web/controller/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .externalController import * -from .internalController import * diff --git a/REPORTER_WORKER/web/controller/blueprint.py b/REPORTER_WORKER/web/controller/blueprint.py deleted file mode 100644 index ca491ac..0000000 --- a/REPORTER_WORKER/web/controller/blueprint.py +++ /dev/null @@ -1,4 +0,0 @@ -from flask import Blueprint - -internal_api = Blueprint('internal_api', __name__) -external_api = Blueprint('external_api', __name__) diff --git a/REPORTER_WORKER/web/controller/externalController.py b/REPORTER_WORKER/web/controller/externalController.py deleted file mode 100644 index 1e62f2f..0000000 --- a/REPORTER_WORKER/web/controller/externalController.py +++ /dev/null @@ -1,29 +0,0 @@ -from datetime import datetime - -import jwt -from flask import send_file, request -from werkzeug.utils import secure_filename - -from web.controller.blueprint import external_api -from web.service.envService import envService - - -@external_api.route("/") -def sendfile(): - token = request.values["token"] - - decoded = jwt.decode(token, envService.JWT_SECRET, algorithms=["HS256"]) - secure_path = secure_filename(decoded["reportID"]) + "/" + secure_filename(decoded["filename"]) - - decoded_expired_time = datetime.fromtimestamp(decoded["valid"]) - - if datetime.now() > decoded_expired_time: - print(datetime.now(), ">", decoded_expired_time) - print() - - return "Not found", 404 - else: - print(datetime.now(), "<", decoded_expired_time) - print(token) - print(secure_path) - return send_file(f"localstorage/{secure_path}", as_attachment=True) \ No newline at end of file diff --git a/REPORTER_WORKER/web/controller/internalController.py b/REPORTER_WORKER/web/controller/internalController.py deleted file mode 100644 index f69126c..0000000 --- a/REPORTER_WORKER/web/controller/internalController.py +++ /dev/null @@ -1,48 +0,0 @@ -from datetime import datetime, timedelta -from os import listdir -from os.path import isfile, join - -import jwt -from flask import request, url_for -from werkzeug.utils import secure_filename - -from web.controller.blueprint import internal_api -from web.service.envService import envService - - -@internal_api.route("/request", methods=['POST']) -def request_file(): - token = request.json["token"] - filename = secure_filename(request.json["file"]) - report_id = secure_filename(request.json["reportID"]) - - if token != envService.API_SECRET: - return "Authorization failed", 401 - - date_and_time = datetime.now() - # Calling the timedelta() function - time_change = timedelta(minutes=1) - new_time = date_and_time + time_change - - encoded = jwt.encode({"reportID": report_id, "filename": filename, "valid": new_time.timestamp()}, - envService.JWT_SECRET, - algorithm="HS256") - - getfile_url = f"{url_for('external_api.sendfile')}" - - return {"token": encoded, "endpoint": getfile_url, "url": getfile_url + "?token=" + encoded} - - -@internal_api.route("/list") -def listoffiles(): - reportid = request.json["reportID"] - token = request.json["token"] - - if token != envService.API_SECRET: - return "Authorization failed", 401 - - mypath = f"localstorage/{secure_filename(reportid)}" - - onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))] - onlyfiles = filter(lambda filename: filename.endswith("pdf"), onlyfiles) - return list(onlyfiles) diff --git a/REPORTER_WORKER/web/service/dataBaseService.py b/REPORTER_WORKER/web/service/dataBaseService.py deleted file mode 100644 index 0cb10b8..0000000 --- a/REPORTER_WORKER/web/service/dataBaseService.py +++ /dev/null @@ -1,41 +0,0 @@ -# ============================================== -# 1. Подключаем библиотеки Python -# ============================================== -import psycopg2 -import csv - - -class DataBaseService(): - pass - - -databaseService = DataBaseService() - -# ============================================== -# 2. Подключаемся к базе данных PGSQL -# ============================================== -conn = psycopg2.connect(dbname='cb', user='postgres', - password='admin', host='localhost') -# ============================================== -# 3. Получаем данные, кладем их в курсор -# ============================================== -cursor = conn.cursor() -cursor.execute('select * from champsends_10') -# --- Получаем наименования колонок -column_names = [] -for row in cursor.description: - column_names.append(row[0]) -# ============================================== -# 4. Пишем файл CSV с колонками -# ============================================== -with open('filename.csv', 'w', newline='', encoding="utf-8") as filename: - write_filename = csv.writer(filename, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) - write_filename.writerow(column_names) - for row in cursor: - write_filename.writerow(row) -# ============================================== -# 5. Закрываем курсор -# Закрываем соединение с Базой данных -# ============================================== -cursor.close() -conn.close() diff --git a/REPORTER_WORKER/web/service/databaseExportService.py b/REPORTER_WORKER/web/service/databaseExportService.py deleted file mode 100644 index 7df06cd..0000000 --- a/REPORTER_WORKER/web/service/databaseExportService.py +++ /dev/null @@ -1,41 +0,0 @@ -# ============================================== -# 1. Подключаем библиотеки Python -# ============================================== -import psycopg2 -import csv - - -class DataBaseExportService(): - pass - - -dataBaseExportService = DataBaseExportService() - -# ============================================== -# 2. Подключаемся к базе данных PGSQL -# ============================================== -conn = psycopg2.connect(dbname='cb', user='postgres', - password='admin', host='localhost') -# ============================================== -# 3. Получаем данные, кладем их в курсор -# ============================================== -cursor = conn.cursor() -cursor.execute('select * from champsends_10') -# --- Получаем наименования колонок -column_names = [] -for row in cursor.description: - column_names.append(row[0]) -# ============================================== -# 4. Пишем файл CSV с колонками -# ============================================== -with open('filename.csv', 'w', newline='', encoding="utf-8") as filename: - write_filename = csv.writer(filename, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) - write_filename.writerow(column_names) - for row in cursor: - write_filename.writerow(row) -# ============================================== -# 5. Закрываем курсор -# Закрываем соединение с Базой данных -# ============================================== -cursor.close() -conn.close() diff --git a/REPORTER_WORKER/web/service/envService.py b/REPORTER_WORKER/web/service/envService.py deleted file mode 100644 index 9084054..0000000 --- a/REPORTER_WORKER/web/service/envService.py +++ /dev/null @@ -1,18 +0,0 @@ -import os - -from dotenv import load_dotenv - -load_dotenv() - - -class EnvService: - JWT_SECRET = os.getenv("JWT_SECRET", default="jwtsecret") - API_SECRET = os.getenv("API_SECRET", default="internalapiprotection") - PUBLIC_ENDPOINTS_PATH = os.getenv("PUBLIC_ENDPOINTS_PATH", default="storage") - INTERNAL_ENDPOINTS_PATH = os.getenv("INTERNAL_ENDPOINTS_PATH", default="internal") - DATABASE_NAME = os.getenv("DATABASE_NAME", default="cb") - DATABASE_USER = os.getenv("DATABASE_USER", default="postgres") - DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD", default="admin") - - -envService = EnvService() diff --git a/REPORTER_WORKER/web/service/jwtService.py b/REPORTER_WORKER/web/service/jwtService.py deleted file mode 100644 index 85262c1..0000000 --- a/REPORTER_WORKER/web/service/jwtService.py +++ /dev/null @@ -1,5 +0,0 @@ -class JWTService: - pass - - -jwtService = JWTService() From cb69714627a71865135e8da147d7b60e6b1da23b Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Wed, 21 May 2025 23:52:03 +0300 Subject: [PATCH 093/117] backend: add profile endpoints --- .../codebattles/backend/dto/UserProfileDto.kt | 8 ++++ .../backend/dto/UserProfileEditDto.kt | 5 +++ .../backend/dto/mapper/UserMapper.kt | 10 ++++- .../backend/dto/mapper/UserProfileMapper.kt | 15 ++++++++ .../web/controllers/ProfileController.kt | 37 +++++++++++++++++++ 5 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserProfileDto.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserProfileEditDto.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserProfileMapper.kt create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProfileController.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserProfileDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserProfileDto.kt new file mode 100644 index 0000000..fee32a7 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserProfileDto.kt @@ -0,0 +1,8 @@ +package ru.codebattles.backend.dto + +data class UserProfileDto( + val id: Long, + val username: String, + val name: String, + val roles: List, +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserProfileEditDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserProfileEditDto.kt new file mode 100644 index 0000000..fb07331 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/UserProfileEditDto.kt @@ -0,0 +1,5 @@ +package ru.codebattles.backend.dto + +data class UserProfileEditDto( + val name: String +) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserMapper.kt index 934d141..212e570 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserMapper.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserMapper.kt @@ -1,9 +1,17 @@ package ru.codebattles.backend.dto.mapper +import org.mapstruct.BeanMapping import org.mapstruct.Mapper +import org.mapstruct.MappingTarget +import org.mapstruct.NullValuePropertyMappingStrategy import ru.codebattles.backend.dto.UserDto +import ru.codebattles.backend.dto.UserProfileEditDto import ru.codebattles.backend.dto.mapper.core.AbstractMapper import ru.codebattles.backend.entity.User + @Mapper(componentModel = "spring") -interface UserMapper : AbstractMapper +interface UserMapper : AbstractMapper { + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + fun updateUserProfile(dto: UserProfileEditDto?, @MappingTarget entity: User?) +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserProfileMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserProfileMapper.kt new file mode 100644 index 0000000..1c320d3 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserProfileMapper.kt @@ -0,0 +1,15 @@ +package ru.codebattles.backend.dto.mapper + +import org.mapstruct.BeanMapping +import org.mapstruct.Mapper +import org.mapstruct.MappingTarget +import org.mapstruct.NullValuePropertyMappingStrategy +import ru.codebattles.backend.dto.UserDto +import ru.codebattles.backend.dto.UserProfileDto +import ru.codebattles.backend.dto.UserProfileEditDto +import ru.codebattles.backend.dto.mapper.core.AbstractMapper +import ru.codebattles.backend.entity.User + + +@Mapper(componentModel = "spring") +interface UserProfileMapper : AbstractMapper \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProfileController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProfileController.kt new file mode 100644 index 0000000..168296e --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/ProfileController.kt @@ -0,0 +1,37 @@ +package ru.codebattles.backend.web.controllers + +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.* +import ru.codebattles.backend.dto.UserProfileDto +import ru.codebattles.backend.dto.UserProfileEditDto +import ru.codebattles.backend.dto.mapper.UserMapper +import ru.codebattles.backend.dto.mapper.UserProfileMapper +import ru.codebattles.backend.entity.User +import ru.codebattles.backend.repository.UserRepository + +@Tag(name = "Profile", description = "Endpoints for current profile") +@RestController +@RequestMapping("/api/profile") +@SecurityRequirement(name = "JWT") +class ProfileController( + private val userRepository: UserRepository, + private val userMapper: UserMapper, + private val userProfileMapper: UserProfileMapper, +) { + + @PutMapping + fun updateProfile(@RequestBody profileData: UserProfileEditDto, @AuthenticationPrincipal user: User): UserProfileDto { + userMapper.updateUserProfile(profileData, user) + userRepository.save(user) + + return userProfileMapper.toDto(user) + } + + @GetMapping + fun getMe(@AuthenticationPrincipal user: User): UserProfileDto { + return userProfileMapper.toDto(user) + } + +} From 7671cfd5f1b6db4883009fb0c05aca901e28af6f Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Wed, 21 May 2025 23:52:13 +0300 Subject: [PATCH 094/117] frontend: add profile --- FRONTEND_V2/src/App.jsx | 2 + FRONTEND_V2/src/components/Header.jsx | 16 ++- FRONTEND_V2/src/pages/user/Profile.jsx | 130 +++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 FRONTEND_V2/src/pages/user/Profile.jsx diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index 5346daa..ac46852 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -27,6 +27,7 @@ import {AdminCheckersEditPage} from "./pages/admin/AdminCheckersEditPage.jsx"; import {AdminProblemsPageCreate} from "./pages/admin/AdminProblemsPageCreate.jsx"; import {AdminProblemsPageEdit} from "./pages/admin/AdminProblemsPageEdit.jsx"; import {AdminCheckersCreatePage} from "./pages/admin/AdminCheckersCreatePage.jsx"; +import {Profile} from "./pages/user/Profile.jsx"; import("../node_modules/bootstrap/dist/js/bootstrap.min.js") @@ -50,6 +51,7 @@ function App() { }/> }/> }/> + }/> }/> }/> diff --git a/FRONTEND_V2/src/components/Header.jsx b/FRONTEND_V2/src/components/Header.jsx index 4ecb6a8..8c4a951 100644 --- a/FRONTEND_V2/src/components/Header.jsx +++ b/FRONTEND_V2/src/components/Header.jsx @@ -5,14 +5,14 @@ import useCachedGetAPI from "../hooks/useGetAPI.js"; import {useEffect} from "react"; - const Header = () => { let navigate = useNavigate(); let isAuthed = localStorage.getItem(constants.LOCALSTORAGE_AUTH_KEY) === "true" - const [profile, update] = useCachedGetAPI("/api/users/me",() => {}, {}); + const [profile, update] = useCachedGetAPI("/api/users/me", () => { + }, {}); console.log(profile) @@ -61,6 +61,13 @@ const Header = () => { } + { + params.pathname === "/profile" && <> +
  • + Соревнования +
  • + + } { compId && compId === "champs" && <> @@ -69,6 +76,11 @@ const Header = () => { {/*Учителю*/} + { + isAuthed && + Профиль + } + { profile.isAdmin && Панель админа diff --git a/FRONTEND_V2/src/pages/user/Profile.jsx b/FRONTEND_V2/src/pages/user/Profile.jsx new file mode 100644 index 0000000..4ed3c18 --- /dev/null +++ b/FRONTEND_V2/src/pages/user/Profile.jsx @@ -0,0 +1,130 @@ +import Card from "../../components/bootstrap/Card.jsx"; +import {useEffect, useState} from "react"; +import {useNavigate} from "react-router-dom"; +import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; +import {useForm} from "react-hook-form"; +import constants from "../../utils/consts.js"; +import axios from "axios"; +import useCachedGetAPI from "../../hooks/useGetAPI.js"; + +export const Profile = () => { + const navigate = useNavigate(); + + const [userProfile, updateData] = useCachedGetAPI(`/api/profile`, () => { + }, []); + + const [loading, setLoading] = useState(false) + const [showSuccess, setShowSuccess] = useState(false) + + const { + register, + handleSubmit, + reset, + formState: {errors} + } = useForm(); + + useEffect(() => { + updateData(); + }, []); + + useEffect(() => { + if (userProfile) { + reset({ + id: userProfile.id, + username: userProfile.username, + name: userProfile.name, + roles: userProfile.roles?.join(', ') + }); + } + }, [userProfile]); + + const onSubmit = (data) => { + const conf = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` + } + }; + + setLoading(true) + setShowSuccess(false) + + axios.put(`/api/profile`, {name: data.name}, conf) + .then(() => setShowSuccess(true)) + .finally(() => setLoading(false)) + }; + + return ( + <> + + + + + + + + +

    Профиль

    + { + showSuccess && +
    + Изменения сохранены +
    + } + +
    + +
    + + +
    + +
    + + +
    + +
    + + + {errors.name &&
    {errors.name.message}
    } +
    + +
    + + +
    + + +
    +
    + + ); +}; From fe842810c85b15df345e98d0d0f9cb46ae018deb Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Thu, 22 May 2025 10:39:42 +0300 Subject: [PATCH 095/117] frontend: add form creation components --- .../src/components/forms/FormContext.jsx | 3 + .../src/components/forms/MasterForm.jsx | 34 ++++++++ .../src/components/forms/TextFormElement.jsx | 36 ++++++++ FRONTEND_V2/src/pages/user/LoginPage.jsx | 85 +++++++++++-------- 4 files changed, 123 insertions(+), 35 deletions(-) create mode 100644 FRONTEND_V2/src/components/forms/FormContext.jsx create mode 100644 FRONTEND_V2/src/components/forms/MasterForm.jsx create mode 100644 FRONTEND_V2/src/components/forms/TextFormElement.jsx diff --git a/FRONTEND_V2/src/components/forms/FormContext.jsx b/FRONTEND_V2/src/components/forms/FormContext.jsx new file mode 100644 index 0000000..dcda2ba --- /dev/null +++ b/FRONTEND_V2/src/components/forms/FormContext.jsx @@ -0,0 +1,3 @@ +import {createContext} from "react"; + +export const FormContext = createContext(null); \ No newline at end of file diff --git a/FRONTEND_V2/src/components/forms/MasterForm.jsx b/FRONTEND_V2/src/components/forms/MasterForm.jsx new file mode 100644 index 0000000..11363dd --- /dev/null +++ b/FRONTEND_V2/src/components/forms/MasterForm.jsx @@ -0,0 +1,34 @@ +import React from 'react'; +import {FormContext} from "./FormContext.jsx"; +import PropTypes from "prop-types"; + +const emptyFunction = () => { +} + +export const MasterForm = ( + {form, onSubmit = emptyFunction, children} +) => { + + // const form = useForm(); + + const { + register, + handleSubmit, + formState: {errors, isSubmitting} + } = form + + + return ( + +
    + {children} +
    +
    + ); +}; + +MasterForm.propTypes = { + form: PropTypes.any, + onSubmit: PropTypes.func, + children: PropTypes.node +}; \ No newline at end of file diff --git a/FRONTEND_V2/src/components/forms/TextFormElement.jsx b/FRONTEND_V2/src/components/forms/TextFormElement.jsx new file mode 100644 index 0000000..9f2b86a --- /dev/null +++ b/FRONTEND_V2/src/components/forms/TextFormElement.jsx @@ -0,0 +1,36 @@ +import React, {useContext, useId} from 'react'; +import {FormContext} from "./FormContext.jsx"; +import PropTypes from "prop-types"; + +export const TextFormElement = ({displayName, name, args}) => { + const formInputId = useId() + + const form = useContext(FormContext); + + const { + register, + formState: {errors} + } = form + + + return ( +
    + + + {errors[name] && ( +
    {errors[name].message}
    + )} +
    + ); +}; + + +TextFormElement.propTypes = { + displayName: PropTypes.string, + name: PropTypes.string.isRequired, + args: PropTypes.object, +} diff --git a/FRONTEND_V2/src/pages/user/LoginPage.jsx b/FRONTEND_V2/src/pages/user/LoginPage.jsx index 8aca6e9..05281d7 100644 --- a/FRONTEND_V2/src/pages/user/LoginPage.jsx +++ b/FRONTEND_V2/src/pages/user/LoginPage.jsx @@ -4,54 +4,69 @@ import {useNavigate} from "react-router-dom"; import {useForm} from "react-hook-form"; import axios from "axios"; import constants from "../../utils/consts.js"; +import {MasterForm} from "../../components/forms/MasterForm.jsx"; +import {TextFormElement} from "../../components/forms/TextFormElement.jsx"; const LoginPage = () => { + const navigate = useNavigate(); - let navigate = useNavigate(); - const {register, handleSubmit} = useForm() + const form = useForm(); - const [hasErrorInData, setHasErrorInData] = useState(false); + const { + formState: {isSubmitting} + } = form - const onSubmit = (data) => { - // console.log(data) - axios.post('/api/auth/login', data) - .then((req) => localStorage.setItem(constants.LOCALSTORAGE_JWT, req.data.token)) - .then((dta) => console.log(dta)) - .then(() => localStorage.setItem(constants.LOCALSTORAGE_AUTH_KEY, "true")) - .then(() => navigate("/champs")) - .catch(() => setHasErrorInData(true)) - } + const [apiError, setApiError] = useState(""); + + const onSubmit = async (data) => { + setApiError(""); // сбрасываем ошибку перед новым запросом + try { + const response = await axios.post('/api/auth/login', data); + const {token} = response.data; + localStorage.setItem(constants.LOCALSTORAGE_JWT, token); + localStorage.setItem(constants.LOCALSTORAGE_AUTH_KEY, "true"); + navigate("/champs"); + } catch (error) { + if (error.response && error.response.status === 401) { + setApiError("Неправильный логин или пароль."); + } else { + setApiError("Произошла ошибка. Попробуйте снова позже."); + } + } + }; useEffect(() => { - if (localStorage.getItem(constants.LOCALSTORAGE_AUTH_KEY) === "true") navigate("/champs") - }, []); + if (localStorage.getItem(constants.LOCALSTORAGE_AUTH_KEY) === "true") { + navigate("/champs"); + } + }, [navigate]); return (
    + -
    -

    Войти в соревнование

    - { - hasErrorInData && (

    Неправильные данные

    ) - } - {/*
    */} - {/* */} - {/* */} - {/*
    */} -
    - - -
    -
    - - +

    Войти в соревнование

    + {apiError && +
    + {apiError}
    + } + + + - - + +
    ); From ce15c13e70e5ba5f9edc0efefe19c2b247f0d137 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Thu, 22 May 2025 10:41:39 +0300 Subject: [PATCH 096/117] frontend: refactor lint errors --- FRONTEND_V2/src/components/forms/MasterForm.jsx | 7 +------ FRONTEND_V2/src/pages/user/Profile.jsx | 4 ---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/FRONTEND_V2/src/components/forms/MasterForm.jsx b/FRONTEND_V2/src/components/forms/MasterForm.jsx index 11363dd..b1b383c 100644 --- a/FRONTEND_V2/src/components/forms/MasterForm.jsx +++ b/FRONTEND_V2/src/components/forms/MasterForm.jsx @@ -9,17 +9,12 @@ export const MasterForm = ( {form, onSubmit = emptyFunction, children} ) => { - // const form = useForm(); - const { - register, handleSubmit, - formState: {errors, isSubmitting} } = form - return ( - +
    {children}
    diff --git a/FRONTEND_V2/src/pages/user/Profile.jsx b/FRONTEND_V2/src/pages/user/Profile.jsx index 4ed3c18..c6e1c01 100644 --- a/FRONTEND_V2/src/pages/user/Profile.jsx +++ b/FRONTEND_V2/src/pages/user/Profile.jsx @@ -1,18 +1,14 @@ import Card from "../../components/bootstrap/Card.jsx"; import {useEffect, useState} from "react"; -import {useNavigate} from "react-router-dom"; import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; -import {AdminHeader} from "../../components/AdminHeader.jsx"; import {useForm} from "react-hook-form"; import constants from "../../utils/consts.js"; import axios from "axios"; import useCachedGetAPI from "../../hooks/useGetAPI.js"; export const Profile = () => { - const navigate = useNavigate(); - const [userProfile, updateData] = useCachedGetAPI(`/api/profile`, () => { }, []); From 37220e767f2572d88561e7ced8d55bc6fbd70179 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Thu, 22 May 2025 10:49:44 +0300 Subject: [PATCH 097/117] Revert "frontend: refactor lint errors" This reverts commit ce15c13e70e5ba5f9edc0efefe19c2b247f0d137. --- FRONTEND_V2/src/components/forms/MasterForm.jsx | 7 ++++++- FRONTEND_V2/src/pages/user/Profile.jsx | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/FRONTEND_V2/src/components/forms/MasterForm.jsx b/FRONTEND_V2/src/components/forms/MasterForm.jsx index b1b383c..11363dd 100644 --- a/FRONTEND_V2/src/components/forms/MasterForm.jsx +++ b/FRONTEND_V2/src/components/forms/MasterForm.jsx @@ -9,12 +9,17 @@ export const MasterForm = ( {form, onSubmit = emptyFunction, children} ) => { + // const form = useForm(); + const { + register, handleSubmit, + formState: {errors, isSubmitting} } = form + return ( - +
    {children}
    diff --git a/FRONTEND_V2/src/pages/user/Profile.jsx b/FRONTEND_V2/src/pages/user/Profile.jsx index c6e1c01..4ed3c18 100644 --- a/FRONTEND_V2/src/pages/user/Profile.jsx +++ b/FRONTEND_V2/src/pages/user/Profile.jsx @@ -1,14 +1,18 @@ import Card from "../../components/bootstrap/Card.jsx"; import {useEffect, useState} from "react"; +import {useNavigate} from "react-router-dom"; import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; import {useForm} from "react-hook-form"; import constants from "../../utils/consts.js"; import axios from "axios"; import useCachedGetAPI from "../../hooks/useGetAPI.js"; export const Profile = () => { + const navigate = useNavigate(); + const [userProfile, updateData] = useCachedGetAPI(`/api/profile`, () => { }, []); From 5151e3e8062e661a66d61f58d0ef81293a242e05 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Thu, 22 May 2025 22:22:24 +0300 Subject: [PATCH 098/117] backend: add put for competitions --- .../backend/dto/mapper/CompetitionsMapper.kt | 9 +++++++- .../codebattles/backend/entity/Competition.kt | 16 +++++++------- .../web/controllers/CompetitionsController.kt | 22 ++++++++++++++++++- .../backend/web/entity/CompetitionEditDto.kt | 19 ++++++++++++++++ 4 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/CompetitionEditDto.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsMapper.kt index 1b7b7c8..c93205c 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsMapper.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/CompetitionsMapper.kt @@ -1,9 +1,16 @@ package ru.codebattles.backend.dto.mapper +import org.mapstruct.BeanMapping import org.mapstruct.Mapper +import org.mapstruct.MappingTarget +import org.mapstruct.NullValuePropertyMappingStrategy import ru.codebattles.backend.entity.Competition import ru.codebattles.backend.dto.CompetitionDto import ru.codebattles.backend.dto.mapper.core.AbstractMapper +import ru.codebattles.backend.web.entity.CompetitionEditDto @Mapper(componentModel = "spring") -interface CompetitionsMapper : AbstractMapper +interface CompetitionsMapper : AbstractMapper { + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + fun update(dto: CompetitionEditDto?, @MappingTarget entity: Competition?) +} diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt index 4274850..2802746 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/entity/Competition.kt @@ -13,27 +13,27 @@ data class Competition( var checkers: MutableSet? = mutableSetOf(), @ManyToOne - val organizer: User?, + var organizer: User?, @Column(nullable = false) - val name: String, + var name: String, @Column(nullable = false, length = 1000) - val description: String, + var description: String, @Column(name = "started_at") - val startedAt: Date? = null, + var startedAt: Date? = null, @Column(name = "ended_at") - val endedAt: Date? = null, + var endedAt: Date? = null, @Column(name = "show_rating", nullable = false) - val showRating: Boolean = true, + var showRating: Boolean = true, @Column(name = "show_output", nullable = false) - val showOutput: Boolean = true, + var showOutput: Boolean = true, @Column(name = "show_input", nullable = false) - val showInput: Boolean = true, + var showInput: Boolean = true, ) : BaseEntity() diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt index 76ba939..b53bdc1 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CompetitionsController.kt @@ -11,10 +11,13 @@ import org.springframework.web.bind.annotation.* import ru.codebattles.backend.annotations.CompetitionAccessRequired import ru.codebattles.backend.annotations.CompetitionId import ru.codebattles.backend.dto.* +import ru.codebattles.backend.dto.mapper.CompetitionsMapper import ru.codebattles.backend.entity.Leaderboard import ru.codebattles.backend.entity.User +import ru.codebattles.backend.repository.CompetitionRepository import ru.codebattles.backend.services.AnswerService import ru.codebattles.backend.services.CompetitionService +import ru.codebattles.backend.web.entity.CompetitionEditDto import ru.codebattles.backend.web.entity.EditUsersRequest import ru.codebattles.backend.web.entity.SendAnswerRequest @@ -22,7 +25,12 @@ import ru.codebattles.backend.web.entity.SendAnswerRequest @RestController @RequestMapping("/api/competitions") @SecurityRequirement(name = "JWT") -class CompetitionsController { +class CompetitionsController ( + private val competitionMapper: CompetitionsMapper +) { + @Autowired + private lateinit var competitionRepository: CompetitionRepository + @Autowired private lateinit var competitionService: CompetitionService @@ -108,6 +116,18 @@ class CompetitionsController { competitionService.patchUsers(compId, data.usersIds) } + @RolesAllowed("ADMIN") + @PutMapping("{compId}") + fun update(@PathVariable compId: Long, @RequestBody profileData: CompetitionEditDto): CompetitionDto { + val competition = competitionRepository.getById(compId) + + competitionMapper.update(profileData, competition) + competitionRepository.save(competition) + + return competitionMapper.toDto(competition) + } + + @Operation( summary = "[ADMIN] Edit competition checkers", description = "Updates the list of checkers for a specific competition. Required admin role." diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/CompetitionEditDto.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/CompetitionEditDto.kt new file mode 100644 index 0000000..f9ee869 --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/entity/CompetitionEditDto.kt @@ -0,0 +1,19 @@ +package ru.codebattles.backend.web.entity + +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +data class CompetitionEditDto( + + @Schema(description = "Name of the competition", example = "Code Battles 2023") + val name: String, + + @Schema(description = "Description of the competition", example = "A competitive coding event") + val description: String, + + @Schema(description = "Start time of the competition", example = "2023-01-01T10:00:00") + val startedAt: LocalDateTime? = null, + + @Schema(description = "End time of the competition", example = "2023-01-01T18:00:00") + val endedAt: LocalDateTime? = null +) \ No newline at end of file From 5918407c614676f9e20694137b43eca33e287b01 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Thu, 22 May 2025 22:45:17 +0300 Subject: [PATCH 099/117] frontend: refactor competitions forms --- .../src/components/CompetitionCard.jsx | 21 +++++ .../components/form_impl/CompetitionForm.jsx | 40 +++++++++ ...xtFormElement.jsx => InputFormElement.jsx} | 15 +++- .../src/components/forms/MasterForm.jsx | 4 +- .../src/pages/admin/AdminChampsCreate.jsx | 60 ++------------ .../src/pages/admin/AdminChampsDetailPage.jsx | 81 +++++++++++-------- .../src/pages/admin/AdminChampsPage.jsx | 18 +++-- FRONTEND_V2/src/pages/user/ChampsPage.jsx | 12 +-- FRONTEND_V2/src/pages/user/LoginPage.jsx | 6 +- FRONTEND_V2/src/utils/settings.js | 2 +- 10 files changed, 152 insertions(+), 107 deletions(-) create mode 100644 FRONTEND_V2/src/components/CompetitionCard.jsx create mode 100644 FRONTEND_V2/src/components/form_impl/CompetitionForm.jsx rename FRONTEND_V2/src/components/forms/{TextFormElement.jsx => InputFormElement.jsx} (63%) diff --git a/FRONTEND_V2/src/components/CompetitionCard.jsx b/FRONTEND_V2/src/components/CompetitionCard.jsx new file mode 100644 index 0000000..9f681b6 --- /dev/null +++ b/FRONTEND_V2/src/components/CompetitionCard.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import Card from "./bootstrap/Card.jsx"; +import PropTypes from "prop-types"; + +export const CompetitionCard = ({name, description, children}) => { + return ( + +
    Идет
    +

    {name}

    +

    {description}

    + + {children} +
    + ); +}; + +CompetitionCard.propTypes = { + name: PropTypes.string.isRequired, + description: PropTypes.string.isRequired, + children: PropTypes.node, +} \ No newline at end of file diff --git a/FRONTEND_V2/src/components/form_impl/CompetitionForm.jsx b/FRONTEND_V2/src/components/form_impl/CompetitionForm.jsx new file mode 100644 index 0000000..a9aeabc --- /dev/null +++ b/FRONTEND_V2/src/components/form_impl/CompetitionForm.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import {InputFormElement} from "../forms/InputFormElement.jsx"; + +export const CompetitionFormElements = () => { + return ( + <> + + + + + + + + + ); +}; + diff --git a/FRONTEND_V2/src/components/forms/TextFormElement.jsx b/FRONTEND_V2/src/components/forms/InputFormElement.jsx similarity index 63% rename from FRONTEND_V2/src/components/forms/TextFormElement.jsx rename to FRONTEND_V2/src/components/forms/InputFormElement.jsx index 9f2b86a..94545d9 100644 --- a/FRONTEND_V2/src/components/forms/TextFormElement.jsx +++ b/FRONTEND_V2/src/components/forms/InputFormElement.jsx @@ -2,7 +2,7 @@ import React, {useContext, useId} from 'react'; import {FormContext} from "./FormContext.jsx"; import PropTypes from "prop-types"; -export const TextFormElement = ({displayName, name, args}) => { +export const InputFormElement = ({displayName, name, args, helpText, readonly = false, disabled = false, type="text"}) => { const formInputId = useId() const form = useContext(FormContext); @@ -20,7 +20,14 @@ export const TextFormElement = ({displayName, name, args}) => { id={formInputId} className={`form-control ${errors[name] ? 'is-invalid' : ''}`} {...register(name, args)} + disabled={disabled} + readOnly={readonly} + type={type} /> + {helpText && + {helpText} + } + {errors[name] && (
    {errors[name].message}
    )} @@ -29,8 +36,12 @@ export const TextFormElement = ({displayName, name, args}) => { }; -TextFormElement.propTypes = { +InputFormElement.propTypes = { displayName: PropTypes.string, name: PropTypes.string.isRequired, + helpText: PropTypes.string, args: PropTypes.object, + readonly: PropTypes.bool, + disabled: PropTypes.bool, + type: PropTypes.string, } diff --git a/FRONTEND_V2/src/components/forms/MasterForm.jsx b/FRONTEND_V2/src/components/forms/MasterForm.jsx index 11363dd..5aa704b 100644 --- a/FRONTEND_V2/src/components/forms/MasterForm.jsx +++ b/FRONTEND_V2/src/components/forms/MasterForm.jsx @@ -20,7 +20,7 @@ export const MasterForm = ( return ( -
    + {children}
    @@ -28,7 +28,7 @@ export const MasterForm = ( }; MasterForm.propTypes = { - form: PropTypes.any, + form: PropTypes.any.isRequired, onSubmit: PropTypes.func, children: PropTypes.node }; \ No newline at end of file diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx index bcf0525..0bc4739 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx @@ -7,18 +7,19 @@ import {AdminHeader} from "../../components/AdminHeader.jsx"; import {useForm} from "react-hook-form"; import axios from "axios"; import constants from "../../utils/consts.js"; +import {CompetitionFormElements} from "../../components/form_impl/CompetitionForm.jsx"; +import {MasterForm} from "../../components/forms/MasterForm.jsx"; export const AdminChampsCreate = () => { - const {register, handleSubmit, formState: {errors}} = useForm({ + const form = useForm({ defaultValues: { - name: '', - description: '', startedAt: new Date().toISOString().slice(0, 16), endedAt: new Date().toISOString().slice(0, 16) } }); + const navigate = useNavigate(); const onSubmit = (data) => { @@ -54,55 +55,10 @@ export const AdminChampsCreate = () => { -
    -
    - - - {errors.name &&

    {errors.name.message}

    } -
    - -
    - - - Описание. Поддерживается Markdown -
    -
    - - - Время начала соревнования -
    -
    - - - Время конца соревнования -
    - -
    + + + + +

    Чекеры

    @@ -82,7 +97,7 @@ export const AdminChampsDetailPage = () => { }) }
    -
    +
    Редактировать чекеры diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx index 6ff1385..a00a274 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx @@ -6,10 +6,12 @@ import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; import {AdminHeader} from "../../components/AdminHeader.jsx"; +import {CompetitionCard} from "../../components/CompetitionCard.jsx"; export const AdminChampsPage = () => { - const [data, update] = useCachedGetAPI("/api/competitions",() => {}, []); + const [data, update] = useCachedGetAPI("/api/competitions", () => { + }, []); useEffect(() => { update() @@ -19,22 +21,22 @@ export const AdminChampsPage = () => { return ( <> - + - + { data?.map(elem => { - return -
    Идет
    -

    {elem.name}

    -

    {elem.description}

    + return Управлять -
    + }) } diff --git a/FRONTEND_V2/src/pages/user/ChampsPage.jsx b/FRONTEND_V2/src/pages/user/ChampsPage.jsx index c483bbe..a72ceac 100644 --- a/FRONTEND_V2/src/pages/user/ChampsPage.jsx +++ b/FRONTEND_V2/src/pages/user/ChampsPage.jsx @@ -1,10 +1,10 @@ -import Card from "../../components/bootstrap/Card.jsx"; import useCachedGetAPI from "../../hooks/useGetAPI.js"; import {useEffect} from "react"; import {Link} from "react-router-dom"; import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {CompetitionCard} from "../../components/CompetitionCard.jsx"; const ChampsPage = () => { @@ -26,12 +26,12 @@ const ChampsPage = () => { { data?.map(elem => { - return -
    Идет
    -

    {elem.name}

    -

    {elem.description}

    + return Войти -
    + }) } diff --git a/FRONTEND_V2/src/pages/user/LoginPage.jsx b/FRONTEND_V2/src/pages/user/LoginPage.jsx index 05281d7..050578e 100644 --- a/FRONTEND_V2/src/pages/user/LoginPage.jsx +++ b/FRONTEND_V2/src/pages/user/LoginPage.jsx @@ -5,7 +5,7 @@ import {useForm} from "react-hook-form"; import axios from "axios"; import constants from "../../utils/consts.js"; import {MasterForm} from "../../components/forms/MasterForm.jsx"; -import {TextFormElement} from "../../components/forms/TextFormElement.jsx"; +import {InputFormElement} from "../../components/forms/InputFormElement.jsx"; const LoginPage = () => { const navigate = useNavigate(); @@ -52,12 +52,12 @@ const LoginPage = () => {
    } - - Date: Thu, 22 May 2025 22:54:56 +0300 Subject: [PATCH 100/117] frontend: comment revert settings --- FRONTEND_V2/src/utils/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FRONTEND_V2/src/utils/settings.js b/FRONTEND_V2/src/utils/settings.js index d193d4f..99b8bb1 100644 --- a/FRONTEND_V2/src/utils/settings.js +++ b/FRONTEND_V2/src/utils/settings.js @@ -2,7 +2,7 @@ import axios from "axios"; // const productionBuild = import.meta.env.VITE_ENV === "production"; // if (!productionBuild) { - axios.defaults.baseURL = `${document.location.protocol}//${document.location.hostname}:${8080}` +// axios.defaults.baseURL = `${document.location.protocol}//${document.location.hostname}:${8080}` // } // const baseUrl = import.meta.env.VITE_API_ENDPOINT // From 74a19f372abc1038d9c163f0ab723ab24fc528cc Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Thu, 22 May 2025 23:07:17 +0300 Subject: [PATCH 101/117] backend: jwt random generated if not set manually --- .../backend/core/properties/JwtTokenProperties.kt | 2 +- .../ru/codebattles/backend/services/JwtService.kt | 5 ++++- .../ru/codebattles/tools/RandomStringGenerator.kt | 11 +++++++++++ BACKEND_V2/src/main/resources/application.properties | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 BACKEND_V2/src/main/kotlin/ru/codebattles/tools/RandomStringGenerator.kt diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/properties/JwtTokenProperties.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/properties/JwtTokenProperties.kt index 987a7c4..8dba134 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/properties/JwtTokenProperties.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/properties/JwtTokenProperties.kt @@ -4,5 +4,5 @@ import org.springframework.boot.context.properties.ConfigurationProperties @ConfigurationProperties("codebattles.jwt") data class JwtTokenProperties ( - val secretKey: String, + val secretKey: String?, ) \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt index 49902d6..f1c49f7 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/JwtService.kt @@ -5,6 +5,7 @@ import io.jsonwebtoken.Jwts import io.jsonwebtoken.security.Keys import org.springframework.stereotype.Service import ru.codebattles.backend.core.properties.JwtTokenProperties +import ru.codebattles.tools.generateSecureRandomString import java.security.Key import java.util.* import javax.crypto.SecretKey @@ -19,7 +20,9 @@ class JwtService( private final fun getSecretKey(): SecretKey { - val secretKey = properties.secretKey + var secretKey = properties.secretKey + if (secretKey == null) secretKey = generateSecureRandomString(128) + val decodedKey = Base64.getDecoder().decode(secretKey) return Keys.hmacShaKeyFor(decodedKey) } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/tools/RandomStringGenerator.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/tools/RandomStringGenerator.kt new file mode 100644 index 0000000..407134b --- /dev/null +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/tools/RandomStringGenerator.kt @@ -0,0 +1,11 @@ +package ru.codebattles.tools + +import java.security.SecureRandom + +fun generateSecureRandomString(length: Int): String { + val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + val secureRandom = SecureRandom() + return (1..length) + .map { chars[secureRandom.nextInt(chars.length)] } + .joinToString("") +} \ No newline at end of file diff --git a/BACKEND_V2/src/main/resources/application.properties b/BACKEND_V2/src/main/resources/application.properties index 565fb3d..8ccc9ad 100644 --- a/BACKEND_V2/src/main/resources/application.properties +++ b/BACKEND_V2/src/main/resources/application.properties @@ -5,7 +5,7 @@ spring.datasource.password=admin spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect -codebattles.jwt.secret-key=alexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalex +#codebattles.jwt.secret-key=alexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalexalex spring.flyway.enabled=true spring.flyway.locations=classpath:db/migrations \ No newline at end of file From d6a0d70f9508761091e1908db5b24caa0ddfdda1 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 23 May 2025 00:13:44 +0300 Subject: [PATCH 102/117] frontend: login page redesign --- FRONTEND_V2/src/pages/user/LoginPage.jsx | 56 ++++++++++++++---------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/FRONTEND_V2/src/pages/user/LoginPage.jsx b/FRONTEND_V2/src/pages/user/LoginPage.jsx index 050578e..26e830e 100644 --- a/FRONTEND_V2/src/pages/user/LoginPage.jsx +++ b/FRONTEND_V2/src/pages/user/LoginPage.jsx @@ -42,32 +42,40 @@ const LoginPage = () => { }, [navigate]); return ( -
    +
    - -

    Войти в соревнование

    - {apiError && -
    - {apiError} -
    - } - - - +
    +
    +
    +
    + +

    Вход в аккаунт

    + {apiError && +
    + {apiError} +
    + } + + + - - -
    + + + +
    +
    +
    +
    ); }; From 162cf81bf1fa5e42853b94b7504b635a929e22a1 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Fri, 23 May 2025 00:25:17 +0300 Subject: [PATCH 103/117] frontend: login hide password --- FRONTEND_V2/src/pages/user/LoginPage.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/FRONTEND_V2/src/pages/user/LoginPage.jsx b/FRONTEND_V2/src/pages/user/LoginPage.jsx index 26e830e..a66d76f 100644 --- a/FRONTEND_V2/src/pages/user/LoginPage.jsx +++ b/FRONTEND_V2/src/pages/user/LoginPage.jsx @@ -64,6 +64,7 @@ const LoginPage = () => { From 32f0ea3cf49d62b8a5d7a71e67fdba5e89960816 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sun, 25 May 2025 21:08:41 +0300 Subject: [PATCH 104/117] frontend: add users management --- FRONTEND_V2/src/App.jsx | 5 + FRONTEND_V2/src/components/AdminHeader.jsx | 3 +- .../pages/admin/users/AdminUserCreatePage.jsx | 135 ++++++++++++++++++ .../src/pages/admin/users/AdminUsersPage.jsx | 57 ++++++++ 4 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 FRONTEND_V2/src/pages/admin/users/AdminUserCreatePage.jsx create mode 100644 FRONTEND_V2/src/pages/admin/users/AdminUsersPage.jsx diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index ac46852..067981a 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -28,6 +28,8 @@ import {AdminProblemsPageCreate} from "./pages/admin/AdminProblemsPageCreate.jsx import {AdminProblemsPageEdit} from "./pages/admin/AdminProblemsPageEdit.jsx"; import {AdminCheckersCreatePage} from "./pages/admin/AdminCheckersCreatePage.jsx"; import {Profile} from "./pages/user/Profile.jsx"; +import {AdminUserCreatePage} from "./pages/admin/users/AdminUserCreatePage.jsx"; +import {AdminUsersPage} from "./pages/admin/users/AdminUsersPage.jsx"; import("../node_modules/bootstrap/dist/js/bootstrap.min.js") @@ -76,6 +78,9 @@ function App() { }/> }/> + }/> + }/> + }/> diff --git a/FRONTEND_V2/src/components/AdminHeader.jsx b/FRONTEND_V2/src/components/AdminHeader.jsx index a3b4730..222a2d9 100644 --- a/FRONTEND_V2/src/components/AdminHeader.jsx +++ b/FRONTEND_V2/src/components/AdminHeader.jsx @@ -5,7 +5,8 @@ export const AdminHeader = () => { return ( соревнования - задачи + задачи + пользователи чекеры интерфейс ученика diff --git a/FRONTEND_V2/src/pages/admin/users/AdminUserCreatePage.jsx b/FRONTEND_V2/src/pages/admin/users/AdminUserCreatePage.jsx new file mode 100644 index 0000000..dbcac0f --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/users/AdminUserCreatePage.jsx @@ -0,0 +1,135 @@ +import React, {useRef, useState} from 'react'; +import {useForm} from 'react-hook-form'; +import Card from "../../../components/bootstrap/Card.jsx"; +import axios from "axios"; +import constants from "../../../utils/consts.js"; +import {useNavigate} from "react-router-dom"; + + +export function AdminUserCreatePage() { + const {register, handleSubmit, setValue} = useForm(); + const [passLength, setPassLength] = useState(12); + const [useSpecials, setUseSpecials] = useState(true); + const modalRef = useRef(null); + + let navigate = useNavigate(); + + const generatePassword = () => { + const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + const specials = '!@#$%^&*()_+~`|}{[]:;?><,./-='; + const chars = charset + (useSpecials ? specials : ''); + return Array.from({length: passLength}, () => + chars.charAt(Math.floor(Math.random() * chars.length)) + ).join(''); + }; + + + const applyPassword = () => { + const newPass = generatePassword(); + setValue('mpassword', newPass); + const modal = bootstrap.Modal.getInstance(modalRef.current); + modal.hide(); + }; + + const onSubmit = (data) => { + axios + .post(`/api/users`, + data, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` + } + }) + .then(() => { + navigate(`/admin/users`) + }) + } + + return ( + <> + + + +

    Форма пользователя

    +
    + +
    + + +
    + +
    + +
    + + +
    +
    + +
    + + +
    + + +
    +
    + + ); +} diff --git a/FRONTEND_V2/src/pages/admin/users/AdminUsersPage.jsx b/FRONTEND_V2/src/pages/admin/users/AdminUsersPage.jsx new file mode 100644 index 0000000..32540a5 --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/users/AdminUsersPage.jsx @@ -0,0 +1,57 @@ +import React, {useEffect} from 'react'; +import Card from "../../../components/bootstrap/Card.jsx"; +import useGetAPI from "../../../hooks/useGetAPI.js"; +import {AdminHeader} from "../../../components/AdminHeader.jsx"; +import {Link} from "react-router-dom"; + + +export function AdminUsersPage() { + + const [users, update] = useGetAPI("/api/users", () => { + }, []) + + useEffect(() => { + update() + }, []); + + return ( + <> + + + +

    Пользователи

    + + + + + + + + + + + { + users.map(elem => { + return + + + + + + }) + } + + +
    IDИмяlogin...
    {elem.id}ИМЯНЕДОСТУПНО{elem.username} + +
    +
    + + + Добавить + + + + + ); +} From 36d54b8b702e33446285b50482471198cc9a116f Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Sun, 25 May 2025 21:31:14 +0300 Subject: [PATCH 105/117] frontend: add id in problems and competitions --- FRONTEND_V2/src/components/CompetitionCard.jsx | 7 +++++-- FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx | 1 + FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx | 1 + FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx | 5 ++++- FRONTEND_V2/src/pages/user/ChampsPage.jsx | 1 + 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/FRONTEND_V2/src/components/CompetitionCard.jsx b/FRONTEND_V2/src/components/CompetitionCard.jsx index 9f681b6..ea535e4 100644 --- a/FRONTEND_V2/src/components/CompetitionCard.jsx +++ b/FRONTEND_V2/src/components/CompetitionCard.jsx @@ -2,11 +2,13 @@ import React from 'react'; import Card from "./bootstrap/Card.jsx"; import PropTypes from "prop-types"; -export const CompetitionCard = ({name, description, children}) => { +export const CompetitionCard = ({name, description, children, id}) => { return (
    Идет
    -

    {name}

    +
    +

    {name}

    id={id || "0000"} +

    {description}

    {children} @@ -18,4 +20,5 @@ CompetitionCard.propTypes = { name: PropTypes.string.isRequired, description: PropTypes.string.isRequired, children: PropTypes.node, + id: PropTypes.string, } \ No newline at end of file diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx index 98e7230..d82652d 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx @@ -70,6 +70,7 @@ export const AdminChampsDetailPage = () => {
    diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx index a00a274..d9914e6 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx @@ -33,6 +33,7 @@ export const AdminChampsPage = () => { data?.map(elem => { return Управлять diff --git a/FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx b/FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx index a7ac5d6..5a236aa 100644 --- a/FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx @@ -31,7 +31,10 @@ export const AdminProblemsPage = () => { data?.map(elem => { return {/*
    Идет
    */} -

    {elem.name}

    +
    +

    {elem.name}

    id={elem.id} +
    +

    {elem.description}

    Управлять
    diff --git a/FRONTEND_V2/src/pages/user/ChampsPage.jsx b/FRONTEND_V2/src/pages/user/ChampsPage.jsx index a72ceac..1933e0f 100644 --- a/FRONTEND_V2/src/pages/user/ChampsPage.jsx +++ b/FRONTEND_V2/src/pages/user/ChampsPage.jsx @@ -28,6 +28,7 @@ const ChampsPage = () => { data?.map(elem => { return Войти From e9b8cca418b603c184605e0fbff87ea8cada8b8b Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Wed, 28 May 2025 14:11:43 +0300 Subject: [PATCH 106/117] frontend: move checker and problems to subdir. fix code duplication in checkers --- FRONTEND_V2/src/App.jsx | 12 +- .../src/components/form_impl/CheckerForm.jsx | 30 +++++ .../pages/admin/AdminCheckersCreatePage.jsx | 92 ---------------- .../src/pages/admin/AdminCheckersEditPage.jsx | 104 ------------------ .../checkers/AdminCheckersCreatePage.jsx | 55 +++++++++ .../admin/checkers/AdminCheckersEditPage.jsx | 81 ++++++++++++++ .../{ => checkers}/AdminCheckersPage.jsx | 12 +- .../{ => problems}/AdminProblemsEdit.jsx | 14 +-- .../{ => problems}/AdminProblemsPage.jsx | 12 +- .../AdminProblemsPageCreate.jsx | 14 +-- .../{ => problems}/AdminProblemsPageEdit.jsx | 14 +-- 11 files changed, 205 insertions(+), 235 deletions(-) create mode 100644 FRONTEND_V2/src/components/form_impl/CheckerForm.jsx delete mode 100644 FRONTEND_V2/src/pages/admin/AdminCheckersCreatePage.jsx delete mode 100644 FRONTEND_V2/src/pages/admin/AdminCheckersEditPage.jsx create mode 100644 FRONTEND_V2/src/pages/admin/checkers/AdminCheckersCreatePage.jsx create mode 100644 FRONTEND_V2/src/pages/admin/checkers/AdminCheckersEditPage.jsx rename FRONTEND_V2/src/pages/admin/{ => checkers}/AdminCheckersPage.jsx (74%) rename FRONTEND_V2/src/pages/admin/{ => problems}/AdminProblemsEdit.jsx (86%) rename FRONTEND_V2/src/pages/admin/{ => problems}/AdminProblemsPage.jsx (75%) rename FRONTEND_V2/src/pages/admin/{ => problems}/AdminProblemsPageCreate.jsx (93%) rename FRONTEND_V2/src/pages/admin/{ => problems}/AdminProblemsPageEdit.jsx (94%) diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index 067981a..523321a 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -12,8 +12,8 @@ import LoginPage from "./pages/user/LoginPage.jsx"; import ChampsPage from "./pages/user/ChampsPage.jsx"; import {AdminChampsPage} from "./pages/admin/AdminChampsPage.jsx"; import {AdminChampsDetailPage} from "./pages/admin/AdminChampsDetailPage.jsx"; -import {AdminProblemsPage} from "./pages/admin/AdminProblemsPage.jsx"; -import {AdminCheckersPage} from "./pages/admin/AdminCheckersPage.jsx"; +import {AdminProblemsPage} from "./pages/admin/problems/AdminProblemsPage.jsx"; +import {AdminCheckersPage} from "./pages/admin/checkers/AdminCheckersPage.jsx"; import {AdminChampsDetailRatingPage} from "./pages/admin/AdminChampsDetailRatingPage.jsx"; import {AdminSeeSendPage} from "./pages/admin/AdminSeeSendPage.jsx"; import {NotFound} from "./pages/NotFound.jsx"; @@ -23,10 +23,10 @@ import {AdminChampsDetailProblemsPage} from "./pages/admin/AdminChampsDetailProb import {AdminChampsDetailProblemsLinkPage} from "./pages/admin/AdminChampsDetailProblemsLinkPage.jsx"; import {AdminChampsDetailProblemsEditPage} from "./pages/admin/AdminChampsDetailProblemsEditPage.jsx"; import {AdminUsersDetailCheckersPage} from "./pages/admin/AdminUsersDetailCheckersPage.jsx"; -import {AdminCheckersEditPage} from "./pages/admin/AdminCheckersEditPage.jsx"; -import {AdminProblemsPageCreate} from "./pages/admin/AdminProblemsPageCreate.jsx"; -import {AdminProblemsPageEdit} from "./pages/admin/AdminProblemsPageEdit.jsx"; -import {AdminCheckersCreatePage} from "./pages/admin/AdminCheckersCreatePage.jsx"; +import {AdminCheckersEditPage} from "./pages/admin/checkers/AdminCheckersEditPage.jsx"; +import {AdminProblemsPageCreate} from "./pages/admin/problems/AdminProblemsPageCreate.jsx"; +import {AdminProblemsPageEdit} from "./pages/admin/problems/AdminProblemsPageEdit.jsx"; +import {AdminCheckersCreatePage} from "./pages/admin/checkers/AdminCheckersCreatePage.jsx"; import {Profile} from "./pages/user/Profile.jsx"; import {AdminUserCreatePage} from "./pages/admin/users/AdminUserCreatePage.jsx"; import {AdminUsersPage} from "./pages/admin/users/AdminUsersPage.jsx"; diff --git a/FRONTEND_V2/src/components/form_impl/CheckerForm.jsx b/FRONTEND_V2/src/components/form_impl/CheckerForm.jsx new file mode 100644 index 0000000..47d09fa --- /dev/null +++ b/FRONTEND_V2/src/components/form_impl/CheckerForm.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import {InputFormElement} from "../forms/InputFormElement.jsx"; + +export const CheckerForm = () => { + return ( + <> + + + + + + + + ); +}; + diff --git a/FRONTEND_V2/src/pages/admin/AdminCheckersCreatePage.jsx b/FRONTEND_V2/src/pages/admin/AdminCheckersCreatePage.jsx deleted file mode 100644 index db84dc6..0000000 --- a/FRONTEND_V2/src/pages/admin/AdminCheckersCreatePage.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import {useForm} from "react-hook-form"; -import axios from "axios"; -import constants from "../../utils/consts.js"; -import {useNavigate} from "react-router-dom"; -import Card from "../../components/bootstrap/Card.jsx"; -import UserLoginRequired from "../../components/UserLoginRequired.jsx"; -import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; -import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; -import {AdminHeader} from "../../components/AdminHeader.jsx"; - -export const AdminCheckersCreatePage = () => { - const navigate = useNavigate(); - - const { - register, - handleSubmit, - formState: {errors} - } = useForm({ - defaultValues: { - displayName: "", - languageHighlightName: "", - address: "" - } - }); - - const onSubmit = (data) => { - const conf = { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` - } - }; - - axios.post("/api/checkers", data, conf) - .then(() => navigate("/admin/checkers")) // Измените путь назначения при необходимости - .catch((error) => console.error("Failed to create checker:", error)); - }; - - return ( - <> - - - - - - - - - -
    -

    Создать чекер

    -
    -
    - - - {errors.displayName && ( -
    {errors.displayName.message}
    - )} -
    - -
    - - - {errors.languageHighlightName && ( -
    {errors.languageHighlightName.message}
    - )} -
    - -
    - - - {errors.address && ( -
    {errors.address.message}
    - )} -
    - - -
    -
    -
    - - ); -}; diff --git a/FRONTEND_V2/src/pages/admin/AdminCheckersEditPage.jsx b/FRONTEND_V2/src/pages/admin/AdminCheckersEditPage.jsx deleted file mode 100644 index a3e3918..0000000 --- a/FRONTEND_V2/src/pages/admin/AdminCheckersEditPage.jsx +++ /dev/null @@ -1,104 +0,0 @@ -import Card from "../../components/bootstrap/Card.jsx"; -import {useEffect} from "react"; -import {useNavigate, useParams} from "react-router-dom"; -import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; -import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; -import UserLoginRequired from "../../components/UserLoginRequired.jsx"; -import {AdminHeader} from "../../components/AdminHeader.jsx"; -import {useForm} from "react-hook-form"; -import constants from "../../utils/consts.js"; -import axios from "axios"; -import useCachedGetAPI from "../../hooks/useGetAPI.js"; - -export const AdminCheckersEditPage = () => { - - - const {checkId} = useParams() - - const navigate = useNavigate(); - - const [competitionsProblem, updateData] = useCachedGetAPI(`/api/checkers/${checkId}/admin`, () => { - }, []); - - const { - register, - handleSubmit, - reset, - formState: {errors} - } = useForm(); - - useEffect(() => { - updateData() - }, []); - - useEffect(() => { - reset({ - displayName: competitionsProblem.displayName, - address: competitionsProblem.address, - languageHighlightName: competitionsProblem.languageHighlightName - }) - }, [competitionsProblem]); - - const onSubmit = (data) => { - console.log(data); - - const conf = { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` - } - } - - axios.patch(`/api/checkers/${checkId}`, data, conf) - .then(() => navigate("/admin/champs")) - - - - }; - - return ( - <> - - - - - - - - - -
    -
    - - - {errors.displayName &&
    {errors.displayName.message}
    } -
    - -
    - - - {errors.languageHighlightName &&
    {errors.languageHighlightName.message}
    } -
    - -
    - - - {errors.address &&
    {errors.address.message}
    } -
    - - -
    - -
    - - ); -}; diff --git a/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersCreatePage.jsx b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersCreatePage.jsx new file mode 100644 index 0000000..ffbe5a3 --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersCreatePage.jsx @@ -0,0 +1,55 @@ +import {useForm} from "react-hook-form"; +import axios from "axios"; +import constants from "../../../utils/consts.js"; +import {useNavigate} from "react-router-dom"; +import Card from "../../../components/bootstrap/Card.jsx"; +import UserLoginRequired from "../../../components/UserLoginRequired.jsx"; +import BreadcrumbsElement from "../../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../../components/BreadcrumpsRoot.jsx"; +import {AdminHeader} from "../../../components/AdminHeader.jsx"; +import {MasterForm} from "../../../components/forms/MasterForm.jsx"; +import {CheckerForm} from "../../../components/form_impl/CheckerForm.jsx"; + +export const AdminCheckersCreatePage = () => { + const navigate = useNavigate(); + + const form = useForm() + + const { + register, + handleSubmit, + formState: {errors} + } = form + + const onSubmit = (data) => { + const conf = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` + } + }; + + axios.post("/api/checkers", data, conf) + .then(() => navigate("/admin/checkers")) // Измените путь назначения при необходимости + .catch((error) => console.error("Failed to create checker:", error)); + }; + + return ( + <> + + + + + + + + + + + + + + + + ); +}; diff --git a/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersEditPage.jsx b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersEditPage.jsx new file mode 100644 index 0000000..7b3c597 --- /dev/null +++ b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersEditPage.jsx @@ -0,0 +1,81 @@ +import Card from "../../../components/bootstrap/Card.jsx"; +import {useEffect} from "react"; +import {useNavigate, useParams} from "react-router-dom"; +import BreadcrumbsElement from "../../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../../components/AdminHeader.jsx"; +import {useForm} from "react-hook-form"; +import constants from "../../../utils/consts.js"; +import axios from "axios"; +import useCachedGetAPI from "../../../hooks/useGetAPI.js"; +import {MasterForm} from "../../../components/forms/MasterForm.jsx"; +import {CheckerForm} from "../../../components/form_impl/CheckerForm.jsx"; + +export const AdminCheckersEditPage = () => { + + + const {checkId} = useParams() + + const navigate = useNavigate(); + + const [competitionsProblem, updateData] = useCachedGetAPI(`/api/checkers/${checkId}/admin`, () => { + }, []); + + const form = useForm(); + + const { + register, + handleSubmit, + reset, + formState: {errors} + } = form + + useEffect(() => { + updateData() + }, []); + + useEffect(() => { + reset({ + displayName: competitionsProblem.displayName, + address: competitionsProblem.address, + languageHighlightName: competitionsProblem.languageHighlightName + }) + }, [competitionsProblem]); + + const onSubmit = (data) => { + console.log(data); + + const conf = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem(constants.LOCALSTORAGE_JWT)}` + } + } + + axios.patch(`/api/checkers/${checkId}`, data, conf) + .then(() => navigate("/admin/champs")) + + + + }; + + return ( + <> + + + + + + + + + + + + + + + + ); +}; diff --git a/FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersPage.jsx similarity index 74% rename from FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx rename to FRONTEND_V2/src/pages/admin/checkers/AdminCheckersPage.jsx index 00650f8..c2ffa40 100644 --- a/FRONTEND_V2/src/pages/admin/AdminCheckersPage.jsx +++ b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersPage.jsx @@ -1,11 +1,11 @@ -import Card from "../../components/bootstrap/Card.jsx"; -import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import Card from "../../../components/bootstrap/Card.jsx"; +import useCachedGetAPI from "../../../hooks/useGetAPI.js"; import {useEffect} from "react"; import {Link} from "react-router-dom"; -import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; -import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; -import UserLoginRequired from "../../components/UserLoginRequired.jsx"; -import {AdminHeader} from "../../components/AdminHeader.jsx"; +import BreadcrumbsElement from "../../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../../components/AdminHeader.jsx"; export const AdminCheckersPage = () => { diff --git a/FRONTEND_V2/src/pages/admin/AdminProblemsEdit.jsx b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsEdit.jsx similarity index 86% rename from FRONTEND_V2/src/pages/admin/AdminProblemsEdit.jsx rename to FRONTEND_V2/src/pages/admin/problems/AdminProblemsEdit.jsx index fe4e40f..24a5b55 100644 --- a/FRONTEND_V2/src/pages/admin/AdminProblemsEdit.jsx +++ b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsEdit.jsx @@ -1,14 +1,14 @@ -import Card from "../../components/bootstrap/Card.jsx"; +import Card from "../../../components/bootstrap/Card.jsx"; import {useEffect} from "react"; import {useNavigate, useParams} from "react-router-dom"; -import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; -import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; -import UserLoginRequired from "../../components/UserLoginRequired.jsx"; -import {AdminHeader} from "../../components/AdminHeader.jsx"; +import BreadcrumbsElement from "../../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../../components/AdminHeader.jsx"; import {useForm} from "react-hook-form"; -import constants from "../../utils/consts.js"; +import constants from "../../../utils/consts.js"; import axios from "axios"; -import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import useCachedGetAPI from "../../../hooks/useGetAPI.js"; export const AdminProblemsEdit = () => { const {probcompId} = useParams() diff --git a/FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPage.jsx similarity index 75% rename from FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx rename to FRONTEND_V2/src/pages/admin/problems/AdminProblemsPage.jsx index 5a236aa..e7da5f1 100644 --- a/FRONTEND_V2/src/pages/admin/AdminProblemsPage.jsx +++ b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPage.jsx @@ -1,11 +1,11 @@ -import Card from "../../components/bootstrap/Card.jsx"; -import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import Card from "../../../components/bootstrap/Card.jsx"; +import useCachedGetAPI from "../../../hooks/useGetAPI.js"; import {useEffect} from "react"; import {Link} from "react-router-dom"; -import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; -import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; -import UserLoginRequired from "../../components/UserLoginRequired.jsx"; -import {AdminHeader} from "../../components/AdminHeader.jsx"; +import BreadcrumbsElement from "../../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../../components/AdminHeader.jsx"; export const AdminProblemsPage = () => { diff --git a/FRONTEND_V2/src/pages/admin/AdminProblemsPageCreate.jsx b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageCreate.jsx similarity index 93% rename from FRONTEND_V2/src/pages/admin/AdminProblemsPageCreate.jsx rename to FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageCreate.jsx index 0986a58..f533110 100644 --- a/FRONTEND_V2/src/pages/admin/AdminProblemsPageCreate.jsx +++ b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageCreate.jsx @@ -1,12 +1,12 @@ -import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import useCachedGetAPI from "../../../hooks/useGetAPI.js"; import {useEffect} from "react"; -import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; -import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; -import UserLoginRequired from "../../components/UserLoginRequired.jsx"; -import {AdminHeader} from "../../components/AdminHeader.jsx"; +import BreadcrumbsElement from "../../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../../components/AdminHeader.jsx"; import {useFieldArray, useForm} from "react-hook-form"; -import Card from "../../components/bootstrap/Card.jsx"; -import constants from "../../utils/consts.js"; +import Card from "../../../components/bootstrap/Card.jsx"; +import constants from "../../../utils/consts.js"; import axios from "axios"; import {useNavigate} from "react-router-dom"; diff --git a/FRONTEND_V2/src/pages/admin/AdminProblemsPageEdit.jsx b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageEdit.jsx similarity index 94% rename from FRONTEND_V2/src/pages/admin/AdminProblemsPageEdit.jsx rename to FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageEdit.jsx index a629679..d2df058 100644 --- a/FRONTEND_V2/src/pages/admin/AdminProblemsPageEdit.jsx +++ b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageEdit.jsx @@ -1,14 +1,14 @@ -import useCachedGetAPI from "../../hooks/useGetAPI.js"; +import useCachedGetAPI from "../../../hooks/useGetAPI.js"; import {useEffect} from "react"; -import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; -import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; -import UserLoginRequired from "../../components/UserLoginRequired.jsx"; -import {AdminHeader} from "../../components/AdminHeader.jsx"; +import BreadcrumbsElement from "../../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../../components/AdminHeader.jsx"; import {useFieldArray, useForm} from "react-hook-form"; -import Card from "../../components/bootstrap/Card.jsx"; +import Card from "../../../components/bootstrap/Card.jsx"; import {useNavigate, useParams} from "react-router-dom"; import axios from "axios"; -import constants from "../../utils/consts.js"; +import constants from "../../../utils/consts.js"; export const AdminProblemsPageEdit = () => { From c58fd888351c28f9a72dbd142900cf5da27823a5 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Wed, 28 May 2025 14:34:55 +0300 Subject: [PATCH 107/117] frontend: fix yarn problems --- FRONTEND_V2/src/components/DeleteButton.jsx | 2 +- FRONTEND_V2/src/components/forms/MasterForm.jsx | 5 ----- .../pages/admin/AdminChampsDetailProblemsPage.jsx | 2 +- .../pages/admin/checkers/AdminCheckersCreatePage.jsx | 6 ------ .../pages/admin/checkers/AdminCheckersEditPage.jsx | 12 +----------- .../src/pages/admin/users/AdminUserCreatePage.jsx | 1 + FRONTEND_V2/src/pages/admin/users/AdminUsersPage.jsx | 2 +- FRONTEND_V2/src/pages/user/Profile.jsx | 4 ---- 8 files changed, 5 insertions(+), 29 deletions(-) diff --git a/FRONTEND_V2/src/components/DeleteButton.jsx b/FRONTEND_V2/src/components/DeleteButton.jsx index 15b97ab..3d4e89b 100644 --- a/FRONTEND_V2/src/components/DeleteButton.jsx +++ b/FRONTEND_V2/src/components/DeleteButton.jsx @@ -40,7 +40,7 @@ export const DeleteButton = ({ } { !disabled && <> -  Удалить + {" "}Удалить } diff --git a/FRONTEND_V2/src/components/forms/MasterForm.jsx b/FRONTEND_V2/src/components/forms/MasterForm.jsx index 5aa704b..e046b4f 100644 --- a/FRONTEND_V2/src/components/forms/MasterForm.jsx +++ b/FRONTEND_V2/src/components/forms/MasterForm.jsx @@ -8,13 +8,8 @@ const emptyFunction = () => { export const MasterForm = ( {form, onSubmit = emptyFunction, children} ) => { - - // const form = useForm(); - const { - register, handleSubmit, - formState: {errors, isSubmitting} } = form diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsPage.jsx b/FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsPage.jsx index 719c02c..8c72eb8 100644 --- a/FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsPage.jsx +++ b/FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsPage.jsx @@ -60,7 +60,7 @@ export const AdminChampsDetailProblemsPage = () => { -   + {" "} Изменить diff --git a/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersCreatePage.jsx b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersCreatePage.jsx index ffbe5a3..e1fce2d 100644 --- a/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersCreatePage.jsx +++ b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersCreatePage.jsx @@ -15,12 +15,6 @@ export const AdminCheckersCreatePage = () => { const form = useForm() - const { - register, - handleSubmit, - formState: {errors} - } = form - const onSubmit = (data) => { const conf = { headers: { diff --git a/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersEditPage.jsx b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersEditPage.jsx index 7b3c597..2201f41 100644 --- a/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersEditPage.jsx +++ b/FRONTEND_V2/src/pages/admin/checkers/AdminCheckersEditPage.jsx @@ -13,8 +13,6 @@ import {MasterForm} from "../../../components/forms/MasterForm.jsx"; import {CheckerForm} from "../../../components/form_impl/CheckerForm.jsx"; export const AdminCheckersEditPage = () => { - - const {checkId} = useParams() const navigate = useNavigate(); @@ -23,20 +21,12 @@ export const AdminCheckersEditPage = () => { }, []); const form = useForm(); - - const { - register, - handleSubmit, - reset, - formState: {errors} - } = form - useEffect(() => { updateData() }, []); useEffect(() => { - reset({ + form.reset({ displayName: competitionsProblem.displayName, address: competitionsProblem.address, languageHighlightName: competitionsProblem.languageHighlightName diff --git a/FRONTEND_V2/src/pages/admin/users/AdminUserCreatePage.jsx b/FRONTEND_V2/src/pages/admin/users/AdminUserCreatePage.jsx index dbcac0f..3c38740 100644 --- a/FRONTEND_V2/src/pages/admin/users/AdminUserCreatePage.jsx +++ b/FRONTEND_V2/src/pages/admin/users/AdminUserCreatePage.jsx @@ -27,6 +27,7 @@ export function AdminUserCreatePage() { const applyPassword = () => { const newPass = generatePassword(); setValue('mpassword', newPass); + // eslint-disable-next-line const modal = bootstrap.Modal.getInstance(modalRef.current); modal.hide(); }; diff --git a/FRONTEND_V2/src/pages/admin/users/AdminUsersPage.jsx b/FRONTEND_V2/src/pages/admin/users/AdminUsersPage.jsx index 32540a5..f3ac511 100644 --- a/FRONTEND_V2/src/pages/admin/users/AdminUsersPage.jsx +++ b/FRONTEND_V2/src/pages/admin/users/AdminUsersPage.jsx @@ -32,7 +32,7 @@ export function AdminUsersPage() { { users.map(elem => { - return + return {elem.id} ИМЯНЕДОСТУПНО {elem.username} diff --git a/FRONTEND_V2/src/pages/user/Profile.jsx b/FRONTEND_V2/src/pages/user/Profile.jsx index 4ed3c18..c6e1c01 100644 --- a/FRONTEND_V2/src/pages/user/Profile.jsx +++ b/FRONTEND_V2/src/pages/user/Profile.jsx @@ -1,18 +1,14 @@ import Card from "../../components/bootstrap/Card.jsx"; import {useEffect, useState} from "react"; -import {useNavigate} from "react-router-dom"; import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; import UserLoginRequired from "../../components/UserLoginRequired.jsx"; -import {AdminHeader} from "../../components/AdminHeader.jsx"; import {useForm} from "react-hook-form"; import constants from "../../utils/consts.js"; import axios from "axios"; import useCachedGetAPI from "../../hooks/useGetAPI.js"; export const Profile = () => { - const navigate = useNavigate(); - const [userProfile, updateData] = useCachedGetAPI(`/api/profile`, () => { }, []); From ec0392f22ec84d1b99bd386ff6bfdd506a5dda80 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Wed, 28 May 2025 14:41:18 +0300 Subject: [PATCH 108/117] backend: fix problems with linter --- .../backend/core/config/SecurityConfig.kt | 30 +------------------ .../backend/dto/mapper/UserProfileMapper.kt | 5 ---- .../backend/repository/ProblemsRepository.kt | 2 +- .../backend/services/CheckerApiService.kt | 6 ++-- .../backend/services/CheckerService.kt | 2 +- .../services/CompetitionsProblemsService.kt | 2 +- .../CheckerSystemEndpointController.kt | 4 +-- 7 files changed, 9 insertions(+), 42 deletions(-) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt index 5254512..b6bcb3d 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/core/config/SecurityConfig.kt @@ -21,40 +21,12 @@ import ru.codebattles.backend.core.filter.JwtAuthenticationFilter @EnableMethodSecurity(jsr250Enabled = true) class SecurityConfig( private val jwtAuthenticationFilter: JwtAuthenticationFilter, - private val passwordEncoder: PasswordEncoder, ) { @Bean fun authenticationManager(authConfig: AuthenticationConfiguration): AuthenticationManager { return authConfig.authenticationManager } -// -// @Bean -// fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { -// http -// .csrf { it.disable() } // Отключить CSRF при необходимости -// .authorizeHttpRequests { -// it -// .requestMatchers("/test/**").permitAll() -// .requestMatchers("/api/auth/login").permitAll() -//// .requestMatchers( -//// "/api/auth/login", -//// "/api/auth/register", -//// "/api/**", -//// "/swagger-ui/**", -//// "/v3/api-docs/**", -//// "/swagger-ui.html", -//// "/favicon.ico", -//// "/webjars/**" -//// ).permitAll()// Разрешить доступ без авторизации -// .anyRequest().authenticated() // Все остальные запросы требуют авторизации -// .and() -// .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) -// } -// .formLogin { it } // Включить стандартный логин через форму -// return http.build() -// } - @Bean fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { @@ -89,7 +61,7 @@ class SecurityConfig( .and() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) .exceptionHandling() - .authenticationEntryPoint(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); + .authenticationEntryPoint(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) return http.build() } diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserProfileMapper.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserProfileMapper.kt index 1c320d3..af4e00a 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserProfileMapper.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/dto/mapper/UserProfileMapper.kt @@ -1,12 +1,7 @@ package ru.codebattles.backend.dto.mapper -import org.mapstruct.BeanMapping import org.mapstruct.Mapper -import org.mapstruct.MappingTarget -import org.mapstruct.NullValuePropertyMappingStrategy -import ru.codebattles.backend.dto.UserDto import ru.codebattles.backend.dto.UserProfileDto -import ru.codebattles.backend.dto.UserProfileEditDto import ru.codebattles.backend.dto.mapper.core.AbstractMapper import ru.codebattles.backend.entity.User diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/ProblemsRepository.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/ProblemsRepository.kt index 76b15aa..c4aa61d 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/ProblemsRepository.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/repository/ProblemsRepository.kt @@ -3,4 +3,4 @@ package ru.codebattles.backend.repository import org.springframework.data.jpa.repository.JpaRepository import ru.codebattles.backend.entity.Problem -interface ProblemsRepository : JpaRepository {} \ No newline at end of file +interface ProblemsRepository : JpaRepository \ No newline at end of file diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CheckerApiService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CheckerApiService.kt index a1b6859..5519e0a 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CheckerApiService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CheckerApiService.kt @@ -15,13 +15,13 @@ class CheckerApiService( ) { fun sendCheckerTask(payload: CheckerTaskRequest, url: String) { - val restTemplate: RestTemplate = RestTemplate() + val restTemplate = RestTemplate() - val headers: HttpHeaders = HttpHeaders() + val headers = HttpHeaders() headers.contentType = MediaType.APPLICATION_JSON val json = objectMapper.writeValueAsString(payload) val entity = HttpEntity(json, headers) - val response = restTemplate.postForEntity( + restTemplate.postForEntity( url, entity, String::class.java ) diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CheckerService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CheckerService.kt index cf7271c..a70152f 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CheckerService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CheckerService.kt @@ -1,4 +1,4 @@ -package ru.codebattles.backend.services; +package ru.codebattles.backend.services import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt index 1675b37..06248a8 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/services/CompetitionsProblemsService.kt @@ -1,4 +1,4 @@ -package ru.codebattles.backend.services; +package ru.codebattles.backend.services import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper diff --git a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CheckerSystemEndpointController.kt b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CheckerSystemEndpointController.kt index b14aadb..6f0fc63 100644 --- a/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CheckerSystemEndpointController.kt +++ b/BACKEND_V2/src/main/kotlin/ru/codebattles/backend/web/controllers/CheckerSystemEndpointController.kt @@ -30,13 +30,13 @@ class CheckerSystemEndpointController( val countOfTests = data.results.size val countOfSuccessTests = data.results.count { it.success } - var score: Int = 0 + var score = 0 if (countOfTests > 0) { score = countOfSuccessTests / countOfTests * 100 } answer.result = objectMapper.writeValueAsString(data) - answer.status = AnswerStatus.COMPLETED; + answer.status = AnswerStatus.COMPLETED answer.score = score From b08337278cf30dc14bef753f2ffa414337734076 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Wed, 28 May 2025 14:45:48 +0300 Subject: [PATCH 109/117] frontend: move champs --- FRONTEND_V2/src/App.jsx | 14 +++++++------- .../pages/{admin => champs}/AdminChampsCreate.jsx | 0 .../{admin => champs}/AdminChampsDetailPage.jsx | 0 .../AdminChampsDetailProblemsEditPage.jsx | 0 .../AdminChampsDetailProblemsLinkPage.jsx | 0 .../AdminChampsDetailProblemsPage.jsx | 0 .../AdminChampsDetailRatingPage.jsx | 0 .../pages/{admin => champs}/AdminChampsPage.jsx | 0 .../problems => champs}/AdminProblemsEdit.jsx | 14 +++++++------- 9 files changed, 14 insertions(+), 14 deletions(-) rename FRONTEND_V2/src/pages/{admin => champs}/AdminChampsCreate.jsx (100%) rename FRONTEND_V2/src/pages/{admin => champs}/AdminChampsDetailPage.jsx (100%) rename FRONTEND_V2/src/pages/{admin => champs}/AdminChampsDetailProblemsEditPage.jsx (100%) rename FRONTEND_V2/src/pages/{admin => champs}/AdminChampsDetailProblemsLinkPage.jsx (100%) rename FRONTEND_V2/src/pages/{admin => champs}/AdminChampsDetailProblemsPage.jsx (100%) rename FRONTEND_V2/src/pages/{admin => champs}/AdminChampsDetailRatingPage.jsx (100%) rename FRONTEND_V2/src/pages/{admin => champs}/AdminChampsPage.jsx (100%) rename FRONTEND_V2/src/pages/{admin/problems => champs}/AdminProblemsEdit.jsx (86%) diff --git a/FRONTEND_V2/src/App.jsx b/FRONTEND_V2/src/App.jsx index 523321a..0485dcc 100644 --- a/FRONTEND_V2/src/App.jsx +++ b/FRONTEND_V2/src/App.jsx @@ -10,18 +10,18 @@ import StatusesPage from "./pages/user/StatusesPage.jsx"; import {BrowserRouter, Route, Routes} from "react-router-dom"; import LoginPage from "./pages/user/LoginPage.jsx"; import ChampsPage from "./pages/user/ChampsPage.jsx"; -import {AdminChampsPage} from "./pages/admin/AdminChampsPage.jsx"; -import {AdminChampsDetailPage} from "./pages/admin/AdminChampsDetailPage.jsx"; +import {AdminChampsPage} from "./pages/champs/AdminChampsPage.jsx"; +import {AdminChampsDetailPage} from "./pages/champs/AdminChampsDetailPage.jsx"; import {AdminProblemsPage} from "./pages/admin/problems/AdminProblemsPage.jsx"; import {AdminCheckersPage} from "./pages/admin/checkers/AdminCheckersPage.jsx"; -import {AdminChampsDetailRatingPage} from "./pages/admin/AdminChampsDetailRatingPage.jsx"; +import {AdminChampsDetailRatingPage} from "./pages/champs/AdminChampsDetailRatingPage.jsx"; import {AdminSeeSendPage} from "./pages/admin/AdminSeeSendPage.jsx"; import {NotFound} from "./pages/NotFound.jsx"; import {AdminUsersDetailPage} from "./pages/admin/AdminUsersDetailPage.jsx"; -import {AdminChampsCreate} from "./pages/admin/AdminChampsCreate.jsx"; -import {AdminChampsDetailProblemsPage} from "./pages/admin/AdminChampsDetailProblemsPage.jsx"; -import {AdminChampsDetailProblemsLinkPage} from "./pages/admin/AdminChampsDetailProblemsLinkPage.jsx"; -import {AdminChampsDetailProblemsEditPage} from "./pages/admin/AdminChampsDetailProblemsEditPage.jsx"; +import {AdminChampsCreate} from "./pages/champs/AdminChampsCreate.jsx"; +import {AdminChampsDetailProblemsPage} from "./pages/champs/AdminChampsDetailProblemsPage.jsx"; +import {AdminChampsDetailProblemsLinkPage} from "./pages/champs/AdminChampsDetailProblemsLinkPage.jsx"; +import {AdminChampsDetailProblemsEditPage} from "./pages/champs/AdminChampsDetailProblemsEditPage.jsx"; import {AdminUsersDetailCheckersPage} from "./pages/admin/AdminUsersDetailCheckersPage.jsx"; import {AdminCheckersEditPage} from "./pages/admin/checkers/AdminCheckersEditPage.jsx"; import {AdminProblemsPageCreate} from "./pages/admin/problems/AdminProblemsPageCreate.jsx"; diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx b/FRONTEND_V2/src/pages/champs/AdminChampsCreate.jsx similarity index 100% rename from FRONTEND_V2/src/pages/admin/AdminChampsCreate.jsx rename to FRONTEND_V2/src/pages/champs/AdminChampsCreate.jsx diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx b/FRONTEND_V2/src/pages/champs/AdminChampsDetailPage.jsx similarity index 100% rename from FRONTEND_V2/src/pages/admin/AdminChampsDetailPage.jsx rename to FRONTEND_V2/src/pages/champs/AdminChampsDetailPage.jsx diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsEditPage.jsx b/FRONTEND_V2/src/pages/champs/AdminChampsDetailProblemsEditPage.jsx similarity index 100% rename from FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsEditPage.jsx rename to FRONTEND_V2/src/pages/champs/AdminChampsDetailProblemsEditPage.jsx diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsLinkPage.jsx b/FRONTEND_V2/src/pages/champs/AdminChampsDetailProblemsLinkPage.jsx similarity index 100% rename from FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsLinkPage.jsx rename to FRONTEND_V2/src/pages/champs/AdminChampsDetailProblemsLinkPage.jsx diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsPage.jsx b/FRONTEND_V2/src/pages/champs/AdminChampsDetailProblemsPage.jsx similarity index 100% rename from FRONTEND_V2/src/pages/admin/AdminChampsDetailProblemsPage.jsx rename to FRONTEND_V2/src/pages/champs/AdminChampsDetailProblemsPage.jsx diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsDetailRatingPage.jsx b/FRONTEND_V2/src/pages/champs/AdminChampsDetailRatingPage.jsx similarity index 100% rename from FRONTEND_V2/src/pages/admin/AdminChampsDetailRatingPage.jsx rename to FRONTEND_V2/src/pages/champs/AdminChampsDetailRatingPage.jsx diff --git a/FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx b/FRONTEND_V2/src/pages/champs/AdminChampsPage.jsx similarity index 100% rename from FRONTEND_V2/src/pages/admin/AdminChampsPage.jsx rename to FRONTEND_V2/src/pages/champs/AdminChampsPage.jsx diff --git a/FRONTEND_V2/src/pages/admin/problems/AdminProblemsEdit.jsx b/FRONTEND_V2/src/pages/champs/AdminProblemsEdit.jsx similarity index 86% rename from FRONTEND_V2/src/pages/admin/problems/AdminProblemsEdit.jsx rename to FRONTEND_V2/src/pages/champs/AdminProblemsEdit.jsx index 24a5b55..fe4e40f 100644 --- a/FRONTEND_V2/src/pages/admin/problems/AdminProblemsEdit.jsx +++ b/FRONTEND_V2/src/pages/champs/AdminProblemsEdit.jsx @@ -1,14 +1,14 @@ -import Card from "../../../components/bootstrap/Card.jsx"; +import Card from "../../components/bootstrap/Card.jsx"; import {useEffect} from "react"; import {useNavigate, useParams} from "react-router-dom"; -import BreadcrumbsElement from "../../../components/BreadcrumbsElement.jsx"; -import BreadcrumbsRoot from "../../../components/BreadcrumpsRoot.jsx"; -import UserLoginRequired from "../../../components/UserLoginRequired.jsx"; -import {AdminHeader} from "../../../components/AdminHeader.jsx"; +import BreadcrumbsElement from "../../components/BreadcrumbsElement.jsx"; +import BreadcrumbsRoot from "../../components/BreadcrumpsRoot.jsx"; +import UserLoginRequired from "../../components/UserLoginRequired.jsx"; +import {AdminHeader} from "../../components/AdminHeader.jsx"; import {useForm} from "react-hook-form"; -import constants from "../../../utils/consts.js"; +import constants from "../../utils/consts.js"; import axios from "axios"; -import useCachedGetAPI from "../../../hooks/useGetAPI.js"; +import useCachedGetAPI from "../../hooks/useGetAPI.js"; export const AdminProblemsEdit = () => { const {probcompId} = useParams() From a6a746a074fbcafb1c4b797941928dd0d9905e17 Mon Sep 17 00:00:00 2001 From: Doctorixx <61980858+Windows-up@users.noreply.github.com> Date: Wed, 28 May 2025 15:18:27 +0300 Subject: [PATCH 110/117] frontend: remove competitions code duplicate --- .../src/components/form_impl/ProblemsForm.jsx | 98 ++++++++++++++ .../admin/problems/AdminProblemsPage.jsx | 1 - .../problems/AdminProblemsPageCreate.jsx | 107 ++------------- .../admin/problems/AdminProblemsPageEdit.jsx | 126 +++--------------- 4 files changed, 131 insertions(+), 201 deletions(-) create mode 100644 FRONTEND_V2/src/components/form_impl/ProblemsForm.jsx diff --git a/FRONTEND_V2/src/components/form_impl/ProblemsForm.jsx b/FRONTEND_V2/src/components/form_impl/ProblemsForm.jsx new file mode 100644 index 0000000..54b3347 --- /dev/null +++ b/FRONTEND_V2/src/components/form_impl/ProblemsForm.jsx @@ -0,0 +1,98 @@ +import React from 'react'; +import {InputFormElement} from "../forms/InputFormElement.jsx"; + +export const ProblemsForm = ({form, testsArray, examplesArray}) => { + const { + register, + } = form + + const {fields: testFields, append: appendTest, remove: removeTest} = testsArray + const {fields: exampleFields, append: appendExample, remove: removeExample} = examplesArray + + return ( + <> + + + + + + + + {/* Tests Section */} +
    + + {testFields.map((item, index) => ( +
    +
    + +
    +
    + +
    +
    + +
    +
    + ))} + +
    + + {/* Examples Section */} +
    + + {exampleFields.map((item, index) => ( +
    +
    + +
    +
    + +
    +
    + +
    +
    + ))} + +
    + + {/* Submit */} + + + ); +}; + diff --git a/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPage.jsx b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPage.jsx index e7da5f1..2e301a5 100644 --- a/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPage.jsx +++ b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPage.jsx @@ -30,7 +30,6 @@ export const AdminProblemsPage = () => { { data?.map(elem => { return - {/*
    Идет
    */}

    {elem.name}

    id={elem.id}
    diff --git a/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageCreate.jsx b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageCreate.jsx index f533110..cc35cc1 100644 --- a/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageCreate.jsx +++ b/FRONTEND_V2/src/pages/admin/problems/AdminProblemsPageCreate.jsx @@ -9,6 +9,8 @@ import Card from "../../../components/bootstrap/Card.jsx"; import constants from "../../../utils/consts.js"; import axios from "axios"; import {useNavigate} from "react-router-dom"; +import {MasterForm} from "../../../components/forms/MasterForm.jsx"; +import {ProblemsForm} from "../../../components/form_impl/ProblemsForm.jsx"; export const AdminProblemsPageCreate = () => { @@ -23,7 +25,7 @@ export const AdminProblemsPageCreate = () => { console.log(data) - const {register, control, handleSubmit, formState: {errors}} = useForm({ + const form = useForm({ defaultValues: { name: "", description: "", @@ -32,18 +34,19 @@ export const AdminProblemsPageCreate = () => { tests: [{in: "", out: ""}], examples: [{in: "", out: ""}] } - }); + }) + const {control} = form - const {fields: testFields, append: appendTest, remove: removeTest} = useFieldArray({ + const testsArray = useFieldArray({ control, name: "tests" }); - - const {fields: exampleFields, append: appendExample, remove: removeExample} = useFieldArray({ + const examplesArray = useFieldArray({ control, name: "examples" }); + const onSubmit = (data) => { const conf = { headers: { @@ -74,97 +77,9 @@ export const AdminProblemsPageCreate = () => {

    Создать задачу

    -
    - - {/* Basic Fields */} -
    - - - {errors.name &&
    {errors.name.message}
    } -
    - -
    - - -
    - -
    - -