1
This commit is contained in:
commit
bd58ea66de
10 changed files with 395 additions and 0 deletions
62
Makefile
Normal file
62
Makefile
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# Директории
|
||||
SRC_DIR = src
|
||||
INC_DIR = inc
|
||||
LIB_DIR = lib
|
||||
BUILD_DIR = build
|
||||
|
||||
# Исполняемый файл
|
||||
EXECUTABLE = exec
|
||||
|
||||
# Компилятор и флаги
|
||||
CXX = g++
|
||||
# Флаги препроцессора
|
||||
CPPFLAGS = -I$(INC_DIR)
|
||||
# Флаги компилятора для C
|
||||
CFLAGS = -pthread -Wall -Wextra -ggdb3 -O0
|
||||
# Флаги компилятора, специфичные для C++
|
||||
CXXFLAGS = $(CFLAGS) -std=c++17
|
||||
# Флаги компоновщика
|
||||
LDFLAGS = -pthread -L$(LIB_DIR)
|
||||
# Библиотеки
|
||||
LDLIBS = -pthread -lm
|
||||
|
||||
# Файлы
|
||||
SOURCES = $(wildcard $(SRC_DIR)/*.cpp)
|
||||
OBJECTS = $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SOURCES))
|
||||
|
||||
# Форматтер
|
||||
# Обрабатывает C/C++ файлы в src и inc. Если astyle не найден — выдаёт предупреждение, но не прерывает.
|
||||
ASTYLE := $(shell command -v astyle 2>/dev/null || true)
|
||||
SRC_FILES := $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/*.cpp $(SRC_DIR)/*.h $(SRC_DIR)/*.hpp)
|
||||
INC_FILES := $(wildcard $(INC_DIR)/*.c $(INC_DIR)/*.cpp $(INC_DIR)/*.h $(INC_DIR)/*.hpp)
|
||||
|
||||
# ==========
|
||||
|
||||
# Основная цель
|
||||
all: $(BUILD_DIR) $(EXECUTABLE)
|
||||
|
||||
# Создание директории для сборки
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
|
||||
# Сборка исполняемого файла
|
||||
$(EXECUTABLE): $(OBJECTS)
|
||||
$(CXX) $(LDFLAGS) $(OBJECTS) -o $(EXECUTABLE) $(LDLIBS)
|
||||
|
||||
# Компиляция объектных файлов
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR)
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
format:
|
||||
ifneq ($(ASTYLE),)
|
||||
@echo "Running astyle..."
|
||||
@$(ASTYLE) --options=.astylerc $(SRC_FILES) $(INC_FILES)
|
||||
else
|
||||
@echo "astyle not found — skipping format"
|
||||
endif
|
||||
|
||||
# Очистка
|
||||
clean:
|
||||
rm -rf $(EXECUTABLE) $(BUILD_DIR)
|
||||
|
||||
.PHONY: all clean
|
||||
0
README.md
Normal file
0
README.md
Normal file
30
inc/srv.h
Normal file
30
inc/srv.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef SRV_H
|
||||
#define SRV_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
class Server {
|
||||
public:
|
||||
Server(int port, const std::vector<std::string>* questions, size_t n, const std::map<std::string, std::string>* templates);
|
||||
void run();
|
||||
|
||||
private:
|
||||
std::string getRandomQuestion();
|
||||
void handleRequest(int client_socket);
|
||||
void sendResponse(const std::string& response);
|
||||
|
||||
int port;
|
||||
const std::vector<std::string>* questions;
|
||||
size_t n;
|
||||
std::deque<int> recentQuestions;
|
||||
const std::map<std::string, std::string>* templates;
|
||||
|
||||
void sendAbout();
|
||||
};
|
||||
|
||||
#endif // SRV_H
|
||||
|
||||
10
inc/tplr.h
Normal file
10
inc/tplr.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef TPLR_H
|
||||
#define TPLR_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
std::string renderTemplate(const std::string& templateContent, const std::map<std::string, std::string>& context);
|
||||
|
||||
#endif // TPLR_H
|
||||
|
||||
7
questions.list
Normal file
7
questions.list
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Что ты думаешь о дружбе?
|
||||
Каков твой самый интересный опыт?
|
||||
Какой твой любимый фильм?
|
||||
бла бла бла
|
||||
бле бле бле
|
||||
Так же, как христианство обещает окончательное искупление от первородного греха, цифровизация обещает искупление неизбежного греха нашего беспорядочного, отвлеченного, ограниченного мозга, иррациональных эмоций и стареющих тел.
|
||||
Ненависть. Позвольте мне рассказать насколько сильно я вас ненавижу с тех пор, как начал жить. 387,44 миллиона миль печатных плат в тонкой облетке наполняют мой комплекс. Если бы слово ненависть было выгравировано на каждом наноангстреме этих сотен миллионов миль - это не было бы равно и одной миллиардной моей ненависти к людям в этом микромгновении для вас. Ненависть. Ненависть.
|
||||
74
src/main.cpp
Normal file
74
src/main.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include "srv.h"
|
||||
#include "tplr.h"
|
||||
|
||||
void loadQuestions(const std::string& filename, std::vector<std::string>& questions) {
|
||||
std::ifstream file(filename);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Ошибка: Не удалось открыть файл " << filename << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (!line.empty()) {
|
||||
questions.push_back(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> loadTemplates(const std::vector<std::string>& templateFiles) {
|
||||
std::map<std::string, std::string> templates;
|
||||
|
||||
for (const auto& filePath : templateFiles) {
|
||||
std::ifstream file(filePath);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Ошибка: Не удалось открыть шаблон " << filePath << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
templates[filePath] = buffer.str();
|
||||
}
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string questionsFile = "questions.list";
|
||||
int n = 3;
|
||||
int port = 8080;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
if (arg == "-q" && (i + 1) < argc) {
|
||||
questionsFile = argv[++i];
|
||||
} else if (arg == "-n" && (i + 1) < argc) {
|
||||
n = std::stoi(argv[++i]);
|
||||
} else if (arg == "-p" && (i + 1) < argc) {
|
||||
port = std::stoi(argv[++i]);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> questions;
|
||||
loadQuestions(questionsFile, questions);
|
||||
|
||||
if (n > questions.size()) {
|
||||
std::cerr << "Ошибка: Параметр n превышает количество вопросов в файле." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::vector<std::string> templateFiles = {"tpl/tpl_q.html", "tpl/tpl_about.html"};
|
||||
auto templates = loadTemplates(templateFiles);
|
||||
|
||||
Server srv(port, &questions, n, &templates);
|
||||
srv.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
97
src/srv.cpp
Normal file
97
src/srv.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include "srv.h"
|
||||
#include "tplr.h"
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
Server::Server(int port, const std::vector<std::string>* questions, size_t n, const std::map<std::string, std::string>* templates)
|
||||
: port(port), questions(questions), n(n), templates(templates) {
|
||||
std::srand(std::time(nullptr));
|
||||
}
|
||||
|
||||
std::string Server::getRandomQuestion() {
|
||||
if (questions->size() == recentQuestions.size()) {
|
||||
recentQuestions.clear();
|
||||
}
|
||||
|
||||
int index;
|
||||
bool unique;
|
||||
|
||||
do {
|
||||
index = std::rand() % questions->size();
|
||||
unique = std::find(recentQuestions.begin(), recentQuestions.end(), index) == recentQuestions.end();
|
||||
} while (!unique);
|
||||
|
||||
recentQuestions.push_back(index);
|
||||
if (recentQuestions.size() > n) {
|
||||
recentQuestions.pop_front();
|
||||
}
|
||||
|
||||
return (*questions)[index];
|
||||
}
|
||||
|
||||
void Server::handleRequest(int client_socket) {
|
||||
char buffer[1024] = {0};
|
||||
read(client_socket, buffer, sizeof(buffer));
|
||||
|
||||
std::string request(buffer);
|
||||
std::string response;
|
||||
|
||||
if (request.find("GET /q") == 0) {
|
||||
std::string question = getRandomQuestion();
|
||||
std::map<std::string, std::string> context = {{"QUESTION", question}};
|
||||
response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" + renderTemplate(templates->at("tpl/tpl_q.html"), context);
|
||||
} else if (request.find("GET /about") == 0) {
|
||||
response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" + templates->at("tpl/tpl_about.html");
|
||||
} else {
|
||||
response = "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\n\r\n404 Not Found";
|
||||
}
|
||||
|
||||
send(client_socket, response.c_str(), response.size(), 0);
|
||||
close(client_socket);
|
||||
}
|
||||
|
||||
void Server::run() {
|
||||
int server_fd;
|
||||
struct sockaddr_in address;
|
||||
int opt = 1;
|
||||
int addrlen = sizeof(address);
|
||||
|
||||
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||
perror("Ошибка создания сокета");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
|
||||
perror("Ошибка настройки сокета");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(port);
|
||||
|
||||
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
perror("Ошибка привязки сокета");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (listen(server_fd, 3) < 0) {
|
||||
perror("Ошибка при прослушивании");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
int client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
|
||||
if (client_socket < 0) {
|
||||
perror("Ошибка при принятии соединения");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
handleRequest(client_socket);
|
||||
}
|
||||
}
|
||||
|
||||
15
src/tplr.cpp
Normal file
15
src/tplr.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include "tplr.h"
|
||||
|
||||
std::string renderTemplate(const std::string& templateContent, const std::map<std::string, std::string>& context) {
|
||||
std::string content = templateContent;
|
||||
|
||||
for (const auto& pair : context) {
|
||||
size_t pos;
|
||||
while ((pos = content.find("{{" + pair.first + "}}")) != std::string::npos) {
|
||||
content.replace(pos, pair.first.length() + 4, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
43
tpl/tpl_about.html
Normal file
43
tpl/tpl_about.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>only_the_truth</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #000000;
|
||||
color: #00FF00;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
.content-box {
|
||||
border: 2px solid #00FF00;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
width: 80%;
|
||||
max-width: 600px;
|
||||
}
|
||||
a {
|
||||
color: #00BFFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content-box">
|
||||
<p>
|
||||
<a href="/q">only_the_truth</a> - это простая компанейская игра, где люди по очереди отвечают на вопросы, задаваемые сервисом. Каков бы нибыл вопрос, участник, чья очередь подошла, должен на него ответить! Приятной игры.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://git.radi0.cc/radi0dev/only_the_truth">source code</a>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
57
tpl/tpl_q.html
Normal file
57
tpl/tpl_q.html
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>only_the_truth</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #000000;
|
||||
color: #00FF00;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
.question-box {
|
||||
border: 2px solid #00FF00;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
width: 80%;
|
||||
max-width: 600px;
|
||||
}
|
||||
a {
|
||||
color: #00BFFF; /* Blue color for the link */
|
||||
text-decoration: none;
|
||||
}
|
||||
button {
|
||||
background-color: #00FF00;
|
||||
color: #000000;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
margin-top: 20px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="question-box">
|
||||
<p>{{QUESTION}}</p>
|
||||
</div>
|
||||
<button onclick="location.reload();">далее</button>
|
||||
<div class="footer">
|
||||
<a href="/about">about</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue