first commit
1
README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
Дипломный проект для курса "Java-разработчик PRO" от Skillbox
|
24
application.yaml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
username: root
|
||||||
|
password: hRMH94dJ
|
||||||
|
url: jdbc:mysql://localhost:3306/search_engine?useSSL=false&requireSSL=false&allowPublicKeyRetrieval=true
|
||||||
|
jpa:
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
||||||
|
# Ниже - временный вариант, таблица удаляется после завершения работы приложения
|
||||||
|
# ddl-auto: create-drop
|
||||||
|
show-sql: true
|
||||||
|
open-in-view: false
|
||||||
|
|
||||||
|
indexing-settings:
|
||||||
|
sites:
|
||||||
|
- url: https://www.lenta.ru
|
||||||
|
name: Лента.ру
|
||||||
|
- url: https://www.skillbox.ru
|
||||||
|
name: Skillbox
|
||||||
|
- url: https://www.playback.ru
|
||||||
|
name: PlayBack.Ru
|
BIN
doc/Критерии оценивания.pdf
Normal file
BIN
doc/ТЗ. Итоговый проект курса «Поисковый движок».pdf
Normal file
BIN
doc/Техническая спецификация.pdf
Normal file
49
pom.xml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>dev.kuksa</groupId>
|
||||||
|
<artifactId>searchengine</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>23</maven.compiler.source>
|
||||||
|
<maven.compiler.target>23</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.3.5</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.34</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<version>8.4.0</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
11
src/main/java/searchengine/Application.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package searchengine;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
}
|
11
src/main/java/searchengine/config/Site.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package searchengine.config;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
public class Site {
|
||||||
|
private String url;
|
||||||
|
private String name;
|
||||||
|
}
|
16
src/main/java/searchengine/config/SitesList.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package searchengine.config;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "indexing-settings")
|
||||||
|
public class SitesList {
|
||||||
|
private List<Site> sites;
|
||||||
|
}
|
31
src/main/java/searchengine/controllers/ApiController.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package searchengine.controllers;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import searchengine.config.SitesList;
|
||||||
|
import searchengine.dto.statistics.StatisticsResponse;
|
||||||
|
import searchengine.services.StatisticsService;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api")
|
||||||
|
public class ApiController {
|
||||||
|
|
||||||
|
private final StatisticsService statisticsService;
|
||||||
|
|
||||||
|
public ApiController(StatisticsService statisticsService) {
|
||||||
|
this.statisticsService = statisticsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/statistics")
|
||||||
|
public ResponseEntity<StatisticsResponse> statistics() {
|
||||||
|
return ResponseEntity.ok(statisticsService.getStatistics());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/startIndexing")
|
||||||
|
public ResponseEntity<StatisticsResponse> startIndexing() {
|
||||||
|
System.out.println(new SitesList().getSites());
|
||||||
|
return ResponseEntity.ok(statisticsService.getStatistics());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package searchengine.controllers;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class DefaultController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод формирует страницу из HTML-файла index.html,
|
||||||
|
* который находится в папке resources/templates.
|
||||||
|
* Это делает библиотека Thymeleaf.
|
||||||
|
*/
|
||||||
|
@RequestMapping("/")
|
||||||
|
public String index() {
|
||||||
|
return "index";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package searchengine.dto.statistics;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class DetailedStatisticsItem {
|
||||||
|
private String url;
|
||||||
|
private String name;
|
||||||
|
private String status;
|
||||||
|
private long statusTime;
|
||||||
|
private String error;
|
||||||
|
private int pages;
|
||||||
|
private int lemmas;
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package searchengine.dto.statistics;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StatisticsData {
|
||||||
|
private TotalStatistics total;
|
||||||
|
private List<DetailedStatisticsItem> detailed;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package searchengine.dto.statistics;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StatisticsResponse {
|
||||||
|
private boolean result;
|
||||||
|
private StatisticsData statistics;
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package searchengine.dto.statistics;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TotalStatistics {
|
||||||
|
private int sites;
|
||||||
|
private int pages;
|
||||||
|
private int lemmas;
|
||||||
|
private boolean indexing;
|
||||||
|
}
|
30
src/main/java/searchengine/model/PageEntity.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package searchengine.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "page", indexes = @Index(columnList = "path"))
|
||||||
|
public class PageEntity {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(nullable = false)
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
@ManyToOne(cascade = CascadeType.ALL)
|
||||||
|
@JoinColumn(name = "site_id", nullable = false)
|
||||||
|
private SiteEntity site;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT NOT NULL, UNIQUE KEY INDEX_PATH (path(255))") // Unique index version
|
||||||
|
// @Column(columnDefinition = "TEXT NOT NULL, INDEX PATH_INDEX USING BTREE(path(255))") // Non-unique index version
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
@Column(nullable = false, columnDefinition = "MEDIUMTEXT")
|
||||||
|
private String content;
|
||||||
|
}
|
34
src/main/java/searchengine/model/SiteEntity.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package searchengine.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "site")
|
||||||
|
public class SiteEntity {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(nullable = false)
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(nullable = false)
|
||||||
|
private StatusType status;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private LocalDateTime status_time;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String last_error;
|
||||||
|
|
||||||
|
@Column(nullable = false, columnDefinition = "VARCHAR(255)")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
@Column(nullable = false, columnDefinition = "VARCHAR(255)")
|
||||||
|
private String name;
|
||||||
|
}
|
7
src/main/java/searchengine/model/StatusType.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package searchengine.model;
|
||||||
|
|
||||||
|
public enum StatusType {
|
||||||
|
INDEXING,
|
||||||
|
INDEXED,
|
||||||
|
FAILED
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package searchengine.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import searchengine.model.PageEntity;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface PageRepository extends CrudRepository<PageEntity, Integer> {
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package searchengine.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import searchengine.model.SiteEntity;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface SiteRepository extends CrudRepository<SiteEntity, Integer> {
|
||||||
|
}
|
4
src/main/java/searchengine/services/IndexingService.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package searchengine.services;
|
||||||
|
|
||||||
|
public interface IndexingService {
|
||||||
|
}
|
16
src/main/java/searchengine/services/IndexingServiceImpl.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package searchengine.services;
|
||||||
|
|
||||||
|
public class IndexingServiceImpl implements IndexingService {
|
||||||
|
boolean indexingIsRunning = false;
|
||||||
|
|
||||||
|
public void startIndexing() {
|
||||||
|
if (indexingIsRunning) {
|
||||||
|
System.out.println("Индексация уже запущена");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Логика для запуска индексации или перенидексации сайтов
|
||||||
|
System.out.println("Запуск индексации");
|
||||||
|
indexingIsRunning = true;
|
||||||
|
System.out.println("Индексация запущена");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package searchengine.services;
|
||||||
|
|
||||||
|
import searchengine.dto.statistics.StatisticsResponse;
|
||||||
|
|
||||||
|
public interface StatisticsService {
|
||||||
|
StatisticsResponse getStatistics();
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package searchengine.services;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import searchengine.config.Site;
|
||||||
|
import searchengine.config.SitesList;
|
||||||
|
import searchengine.dto.statistics.DetailedStatisticsItem;
|
||||||
|
import searchengine.dto.statistics.StatisticsData;
|
||||||
|
import searchengine.dto.statistics.StatisticsResponse;
|
||||||
|
import searchengine.dto.statistics.TotalStatistics;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class StatisticsServiceImpl implements StatisticsService {
|
||||||
|
|
||||||
|
private final Random random = new Random();
|
||||||
|
private final SitesList sites;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StatisticsResponse getStatistics() {
|
||||||
|
String[] statuses = { "INDEXED", "FAILED", "INDEXING" };
|
||||||
|
String[] errors = {
|
||||||
|
"Ошибка индексации: главная страница сайта не доступна",
|
||||||
|
"Ошибка индексации: сайт не доступен",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
TotalStatistics total = new TotalStatistics();
|
||||||
|
total.setSites(sites.getSites().size());
|
||||||
|
total.setIndexing(true);
|
||||||
|
|
||||||
|
List<DetailedStatisticsItem> detailed = new ArrayList<>();
|
||||||
|
List<Site> sitesList = sites.getSites();
|
||||||
|
for(int i = 0; i < sitesList.size(); i++) {
|
||||||
|
Site site = sitesList.get(i);
|
||||||
|
DetailedStatisticsItem item = new DetailedStatisticsItem();
|
||||||
|
item.setName(site.getName());
|
||||||
|
item.setUrl(site.getUrl());
|
||||||
|
int pages = random.nextInt(1_000);
|
||||||
|
int lemmas = pages * random.nextInt(1_000);
|
||||||
|
item.setPages(pages);
|
||||||
|
item.setLemmas(lemmas);
|
||||||
|
item.setStatus(statuses[i % 3]);
|
||||||
|
item.setError(errors[i % 3]);
|
||||||
|
item.setStatusTime(System.currentTimeMillis() -
|
||||||
|
(random.nextInt(10_000)));
|
||||||
|
total.setPages(total.getPages() + pages);
|
||||||
|
total.setLemmas(total.getLemmas() + lemmas);
|
||||||
|
detailed.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
StatisticsResponse response = new StatisticsResponse();
|
||||||
|
StatisticsData data = new StatisticsData();
|
||||||
|
data.setTotal(total);
|
||||||
|
data.setDetailed(detailed);
|
||||||
|
response.setStatistics(data);
|
||||||
|
response.setResult(true);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
2393
src/main/resources/static/assets/css/basic.css
Normal file
1144
src/main/resources/static/assets/css/extra.css
Normal file
117
src/main/resources/static/assets/css/fonts.css
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat Black";
|
||||||
|
src: url("Montserrat-Black.eot"); /* IE9 */
|
||||||
|
src: url("Montserrat-Black.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("Montserrat-Black.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("Montserrat-Black.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("Montserrat-Black.svg#Montserrat Black") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 50 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
src: url("Montserrat-Bold.eot"); /* IE9 */
|
||||||
|
src: url("Montserrat-Bold.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("Montserrat-Bold.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("Montserrat-Bold.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("Montserrat-Bold.svg#Montserrat") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 52 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat ExtraBold";
|
||||||
|
src: url("Montserrat-ExtraBold.eot"); /* IE9 */
|
||||||
|
src: url("Montserrat-ExtraBold.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("Montserrat-ExtraBold.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("Montserrat-ExtraBold.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("Montserrat-ExtraBold.svg#Montserrat ExtraBold") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 51 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat ExtraLight";
|
||||||
|
src: url("Montserrat-ExtraLight.eot"); /* IE9 */
|
||||||
|
src: url("Montserrat-ExtraLight.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("Montserrat-ExtraLight.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("Montserrat-ExtraLight.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("Montserrat-ExtraLight.svg#Montserrat ExtraLight") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat Light";
|
||||||
|
src: url("Montserrat-Light.eot"); /* IE9 */
|
||||||
|
src: url("Montserrat-Light.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("Montserrat-Light.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("Montserrat-Light.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("Montserrat-Light.svg#Montserrat Light") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat Medium";
|
||||||
|
src: url("Montserrat-Medium.eot"); /* IE9 */
|
||||||
|
src: url("Montserrat-Medium.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("Montserrat-Medium.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("Montserrat-Medium.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("Montserrat-Medium.svg#Montserrat Medium") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
src: url("Montserrat-Regular.eot"); /* IE9 */
|
||||||
|
src: url("Montserrat-Regular.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("Montserrat-Regular.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("Montserrat-Regular.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("Montserrat-Regular.svg#Montserrat") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat SemiBold";
|
||||||
|
src: url("Montserrat-SemiBold.eot"); /* IE9 */
|
||||||
|
src: url("Montserrat-SemiBold.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("Montserrat-SemiBold.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("Montserrat-SemiBold.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("Montserrat-SemiBold.svg#Montserrat SemiBold") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 52 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat Thin";
|
||||||
|
src: url("Montserrat-Thin.eot"); /* IE9 */
|
||||||
|
src: url("Montserrat-Thin.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("Montserrat-Thin.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("Montserrat-Thin.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("Montserrat-Thin.svg#Montserrat Thin") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 54 KiB |
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 386.257 386.257"><path d="M0 96.879l193.129 192.5 193.128-192.5z"/></svg>
|
After Width: | Height: | Size: 126 B |
1
src/main/resources/static/assets/img/icons/cancel.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 0C115.3 0 0 115.3 0 256s115.3 256 256 256 256-115.3 256-256S396.7 0 256 0z" fill="#ff3636"/><path d="M512 256c0 140.7-115.3 256-256 256V0c140.7 0 256 115.3 256 256z" fill="#f40000"/><path fill="#e7e7e7" d="M298.299 256l84.901 84.901-42.299 42.299L256 298.299 171.099 383.2 128.8 340.901 213.701 256 128.8 171.099l42.299-42.299L256 213.701l84.901-84.901 42.299 42.299z"/><path fill="#d3d3d8" d="M298.299 256l84.901 84.901-42.299 42.299L256 298.299v-84.598l84.901-84.901 42.299 42.299z"/></svg>
|
After Width: | Height: | Size: 568 B |
1
src/main/resources/static/assets/img/icons/checked.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 0C115.3 0 0 115.3 0 256s115.3 256 256 256 256-115.3 256-256S396.7 0 256 0z" fill="#7d0"/><path d="M512 256c0 140.7-115.3 256-256 256V0c140.7 0 256 115.3 256 256z" fill="#6b0"/><path fill="#e7e7e7" d="M401.8 212.5L226 388.299l-126.301-126 42.602-42.6 83.699 84 30-30L359.5 170.2z"/><path fill="#d3d3d8" d="M401.8 212.5L256 358.299v-84.6L359.5 170.2z"/></svg>
|
After Width: | Height: | Size: 433 B |
1
src/main/resources/static/assets/img/icons/dashboard.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#ffb74f" d="M432.106 250.534v219.487H296.578V336.975h-75.179v133.046H79.894V250.534L256 115.075z"/><path d="M439.485 183.135V90.306h-74.167v35.772L256 41.979 0 238.92l53.633 69.712L256 152.959l202.367 155.672L512 238.92l-72.515-55.785z" fill="#ff7d3c"/><path fill="#ff9a00" d="M432.106 250.534v219.487H296.578V336.975H256v-221.9z"/><path fill="#ff4e19" d="M512 238.92l-53.633 69.712L256 152.959V41.979l109.318 84.099V90.306h74.167v92.829z"/></svg>
|
After Width: | Height: | Size: 521 B |
After Width: | Height: | Size: 12 KiB |
1
src/main/resources/static/assets/img/icons/pause.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M0 256c0 141.384 114.615 256 256 256l22.261-256L256 0C114.615 0 0 114.615 0 256z" fill="#bfdcff"/><path d="M256 0v512c141.384 0 256-114.616 256-256S397.384 0 256 0z" fill="#8bc0ff"/><path fill="#3897ff" d="M155.826 144.696h66.783v222.609h-66.783z"/><path fill="#2d79cc" d="M289.391 144.696h66.783v222.609h-66.783z"/></svg>
|
After Width: | Height: | Size: 393 B |
1
src/main/resources/static/assets/img/icons/search.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg height="512" width="512" xmlns="http://www.w3.org/2000/svg"><path d="M179.907 317.89l63.63-63.63 17.675 17.676-63.63 63.63z" fill="#83a3ab"/><path d="M176.371 314.355l63.63-63.63 10.605 10.605-63.63 63.63zM66.192 403.381l-53.033 95.46c17.545 17.545 46.094 17.546 63.64 0l21.213-21.213z" fill="#93b7c0"/><path d="M34.373 413.988L13.16 435.201c-17.546 17.546-17.545 46.094 0 63.64l74.246-74.246z" fill="#acd5df"/><path d="M98.012 477.627L240.06 335.58l-31.82-31.82-92.237 49.811-49.811 92.237z" fill="#3a6fd8"/><path d="M34.335 413.954L176.36 271.93l31.815 31.815L66.15 445.769z" fill="#3b88f5"/><path d="M463.526 48.474l-175.539 58.513-58.513 175.539c64.632 64.632 169.421 64.632 234.052 0s64.632-169.42 0-234.052z" fill="#93b7c0"/><path d="M229.474 48.474c-64.632 64.632-64.632 169.421 0 234.052L463.526 48.474c-64.632-64.632-169.42-64.632-234.052 0z" fill="#acd5df"/><path d="M421.1 90.9l-111.9 37.3-37.3 111.9c41.2 41.2 107.999 41.2 149.2 0s41.2-107.999 0-149.2z" fill="#c4f3ff"/><path d="M271.9 90.9c-41.2 41.2-41.2 107.999 0 149.2L421.1 90.9c-41.201-41.2-107.999-41.2-149.2 0z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 1.1 KiB |
1954
src/main/resources/static/assets/js/scripts.js
Normal file
1193
src/main/resources/static/assets/plg/form/jquery.form.js
Normal file
7
src/main/resources/static/assets/plg/form/jquery.maskedinput.min.js
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
jQuery Masked Input Plugin
|
||||||
|
Copyright (c) 2007 - 2015 Josh Bush (digitalbush.com)
|
||||||
|
Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
|
||||||
|
Version: 1.4.1
|
||||||
|
*/
|
||||||
|
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b,c=navigator.userAgent,d=/iphone/i.test(c),e=/chrome/i.test(c),f=/android/i.test(c);a.mask={definitions:{9:"[0-9]",a:"[A-Za-z]","*":"[A-Za-z0-9]"},autoclear:!0,dataName:"rawMaskFn",placeholder:"_"},a.fn.extend({caret:function(a,b){var c;if(0!==this.length&&!this.is(":hidden"))return"number"==typeof a?(b="number"==typeof b?b:a,this.each(function(){this.setSelectionRange?this.setSelectionRange(a,b):this.createTextRange&&(c=this.createTextRange(),c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",a),c.select())})):(this[0].setSelectionRange?(a=this[0].selectionStart,b=this[0].selectionEnd):document.selection&&document.selection.createRange&&(c=document.selection.createRange(),a=0-c.duplicate().moveStart("character",-1e5),b=a+c.text.length),{begin:a,end:b})},unmask:function(){return this.trigger("unmask")},mask:function(c,g){var h,i,j,k,l,m,n,o;if(!c&&this.length>0){h=a(this[0]);var p=h.data(a.mask.dataName);return p?p():void 0}return g=a.extend({autoclear:a.mask.autoclear,placeholder:a.mask.placeholder,completed:null},g),i=a.mask.definitions,j=[],k=n=c.length,l=null,a.each(c.split(""),function(a,b){"?"==b?(n--,k=a):i[b]?(j.push(new RegExp(i[b])),null===l&&(l=j.length-1),k>a&&(m=j.length-1)):j.push(null)}),this.trigger("unmask").each(function(){function h(){if(g.completed){for(var a=l;m>=a;a++)if(j[a]&&C[a]===p(a))return;g.completed.call(B)}}function p(a){return g.placeholder.charAt(a<g.placeholder.length?a:0)}function q(a){for(;++a<n&&!j[a];);return a}function r(a){for(;--a>=0&&!j[a];);return a}function s(a,b){var c,d;if(!(0>a)){for(c=a,d=q(b);n>c;c++)if(j[c]){if(!(n>d&&j[c].test(C[d])))break;C[c]=C[d],C[d]=p(d),d=q(d)}z(),B.caret(Math.max(l,a))}}function t(a){var b,c,d,e;for(b=a,c=p(a);n>b;b++)if(j[b]){if(d=q(b),e=C[b],C[b]=c,!(n>d&&j[d].test(e)))break;c=e}}function u(){var a=B.val(),b=B.caret();if(o&&o.length&&o.length>a.length){for(A(!0);b.begin>0&&!j[b.begin-1];)b.begin--;if(0===b.begin)for(;b.begin<l&&!j[b.begin];)b.begin++;B.caret(b.begin,b.begin)}else{for(A(!0);b.begin<n&&!j[b.begin];)b.begin++;B.caret(b.begin,b.begin)}h()}function v(){A(),B.val()!=E&&B.change()}function w(a){if(!B.prop("readonly")){var b,c,e,f=a.which||a.keyCode;o=B.val(),8===f||46===f||d&&127===f?(b=B.caret(),c=b.begin,e=b.end,e-c===0&&(c=46!==f?r(c):e=q(c-1),e=46===f?q(e):e),y(c,e),s(c,e-1),a.preventDefault()):13===f?v.call(this,a):27===f&&(B.val(E),B.caret(0,A()),a.preventDefault())}}function x(b){if(!B.prop("readonly")){var c,d,e,g=b.which||b.keyCode,i=B.caret();if(!(b.ctrlKey||b.altKey||b.metaKey||32>g)&&g&&13!==g){if(i.end-i.begin!==0&&(y(i.begin,i.end),s(i.begin,i.end-1)),c=q(i.begin-1),n>c&&(d=String.fromCharCode(g),j[c].test(d))){if(t(c),C[c]=d,z(),e=q(c),f){var k=function(){a.proxy(a.fn.caret,B,e)()};setTimeout(k,0)}else B.caret(e);i.begin<=m&&h()}b.preventDefault()}}}function y(a,b){var c;for(c=a;b>c&&n>c;c++)j[c]&&(C[c]=p(c))}function z(){B.val(C.join(""))}function A(a){var b,c,d,e=B.val(),f=-1;for(b=0,d=0;n>b;b++)if(j[b]){for(C[b]=p(b);d++<e.length;)if(c=e.charAt(d-1),j[b].test(c)){C[b]=c,f=b;break}if(d>e.length){y(b+1,n);break}}else C[b]===e.charAt(d)&&d++,k>b&&(f=b);return a?z():k>f+1?g.autoclear||C.join("")===D?(B.val()&&B.val(""),y(0,n)):z():(z(),B.val(B.val().substring(0,f+1))),k?b:l}var B=a(this),C=a.map(c.split(""),function(a,b){return"?"!=a?i[a]?p(b):a:void 0}),D=C.join(""),E=B.val();B.data(a.mask.dataName,function(){return a.map(C,function(a,b){return j[b]&&a!=p(b)?a:null}).join("")}),B.one("unmask",function(){B.off(".mask").removeData(a.mask.dataName)}).on("focus.mask",function(){if(!B.prop("readonly")){clearTimeout(b);var a;E=B.val(),a=A(),b=setTimeout(function(){B.get(0)===document.activeElement&&(z(),a==c.replace("?","").length?B.caret(0,a):B.caret(a))},10)}}).on("blur.mask",v).on("keydown.mask",w).on("keypress.mask",x).on("input.mask paste.mask",function(){B.prop("readonly")||setTimeout(function(){var a=A(!0);B.caret(a),h()},0)}),e&&f&&B.off("input.mask").on("input.mask",u),A()})}})});
|
2
src/main/resources/static/assets/plg/jQuery/jquery-3.5.0.slim.min.js
vendored
Normal file
2
src/main/resources/static/assets/plg/jQuery/jquery-3.5.1.min.js
vendored
Normal file
162
src/main/resources/templates/index.html
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
|
||||||
|
<!--END-->
|
||||||
|
<!--END--><!DOCTYPE html><!--[if IE 7]>
|
||||||
|
<html class="ie7" lang="ru">
|
||||||
|
<![endif]-->
|
||||||
|
<!--[if IE 8]>
|
||||||
|
<html class="ie8" lang="ru">
|
||||||
|
<![endif]-->
|
||||||
|
<!--[if IE 9]>
|
||||||
|
<html class="ie9" lang="ru">
|
||||||
|
<![endif]-->
|
||||||
|
<!--[if gt IE 9]><!--> <html lang="ru"> <!--<![endif]-->
|
||||||
|
<head>
|
||||||
|
<title>Site Search Engine</title>
|
||||||
|
<meta name="description" content="Site Search Engine">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||||
|
<!--meta( http-equiv="cache-control" content="no-cache")-->
|
||||||
|
<!--meta( http-equiv="expires" content="0")-->
|
||||||
|
<!--link(rel="preload" href="assets/css/extra.min.css?v=" + version as="style" crossorigin="anonymous")-->
|
||||||
|
<link href="favicon.ico" rel="shortcut icon">
|
||||||
|
<link rel="preload" href="/assets/fonts/Montserrat/Montserrat-SemiBold.woff2" as="font" crossorigin="anonymous">
|
||||||
|
<link rel="preload" href="/assets/fonts/Montserrat/Montserrat-Light.woff2" as="font" crossorigin="anonymous">
|
||||||
|
<link rel="preload" href="/assets/fonts/Montserrat/Montserrat-Medium.woff2" as="font" crossorigin="anonymous">
|
||||||
|
<link rel="preload" href="/assets/fonts/Montserrat/Montserrat-ExtraBold.woff2" as="font" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="/assets/css/fonts.css?v=82368483">
|
||||||
|
<link rel="stylesheet" href="/assets/css/basic.css?v=82368483">
|
||||||
|
<link rel="stylesheet" href="/assets/css/extra.css?v=82368483"><!--[if lt IE 9]>
|
||||||
|
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var backendApiUrl = 'api'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script defer src="/assets/js/scripts.js?v=38874865"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="Site">
|
||||||
|
<!--if lt IE 8
|
||||||
|
p.error-browser
|
||||||
|
| Ваш браузер
|
||||||
|
em устарел!
|
||||||
|
a(href="http://browsehappy.com/") Выберите новую версию
|
||||||
|
+s
|
||||||
|
| браузера здесь
|
||||||
|
| для правильного отображения сайта.
|
||||||
|
-->
|
||||||
|
<div class="Site-loader">
|
||||||
|
<div class="Site-loader-block">
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Middle">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="Site-loadingIsComplete">
|
||||||
|
<main class="Tabs Tabs_column Middle-main">
|
||||||
|
<div class="Column">
|
||||||
|
<div class="Tabs-links"><a class="Tabs-link Tabs-link_ACTIVE" href="#dashboard"><img class="Tabs-icon" src="/assets/img/icons/dashboard.svg" alt="dashboard.svg"/><span class="Tabs-linkText">Dashboard</span></a><a class="Tabs-link" href="#management"><img class="Tabs-icon" src="/assets/img/icons/management.svg" alt="management.svg"/><span class="Tabs-linkText">Management</span></a><a class="Tabs-link" href="#search"><img class="Tabs-icon" src="/assets/img/icons/search.svg" alt="search.svg"/><span class="Tabs-linkText">Search</span></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Tabs-wrap">
|
||||||
|
<div class="Tabs-block" id="dashboard">
|
||||||
|
<div class="Section">
|
||||||
|
<div class="Section-header">
|
||||||
|
<h2 class="Section-title">Dashboard
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="Statistics">
|
||||||
|
<div class="Statistics-info">
|
||||||
|
<div class="Statistics-block"><span class="Statistics-amount" id="totalSites"></span><span class="Statistics-title">sites</span>
|
||||||
|
</div>
|
||||||
|
<div class="Statistics-block"><span class="Statistics-amount" id="totalPages"></span><span class="Statistics-title">pages</span>
|
||||||
|
</div>
|
||||||
|
<div class="Statistics-block"><span class="Statistics-amount" id="totalLemmas"></span><span class="Statistics-title">lemmas</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="HideBlock Statistics-example">
|
||||||
|
<header class="HideBlock-header HideBlock-trigger">
|
||||||
|
<strong class="HideBlock-title"><span class="Statistics-status"></span>
|
||||||
|
</strong>
|
||||||
|
<button class="HideBlock-btn" type="button">
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<div class="HideBlock-content">
|
||||||
|
<p class="Statistics-description">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Tabs-block" id="management">
|
||||||
|
<div class="Section">
|
||||||
|
<div class="Section-header">
|
||||||
|
<h2 class="Section-title">Management
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="btn btn_primary API-startIndexing" data-btntype="check" data-check="false" data-alttext="Stop indexing" data-send="startIndexing" data-altsend="stopIndexing"><span class="btn-content">Start indexing</span>
|
||||||
|
</button>
|
||||||
|
</div><br>
|
||||||
|
<div class="UpdatePageBlock">
|
||||||
|
<h3>Add/update page:
|
||||||
|
</h3>
|
||||||
|
<form class="form form_close" action="#" method="post" data-send="indexPage">
|
||||||
|
<div class="form-group form-group_row">
|
||||||
|
<input class="form-input" id="page" name="page" type="text"/>
|
||||||
|
<button class="btn btn_primary form-btn" type="submit">Add/update
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Tabs-block" id="search">
|
||||||
|
<div class="Section">
|
||||||
|
<div class="Section-header">
|
||||||
|
<h2 class="Section-title">Search
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<form class="form form_close" action="#" method="post" data-send="search" data-sendlimit="10">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-selectWrap">
|
||||||
|
<!-- - var options = setOptions(items, ['value', 'selected', 'disabled']);-->
|
||||||
|
<select class="form-select" name="site">
|
||||||
|
<option value="" selected="selected">All sites
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group form-group_row">
|
||||||
|
<input class="form-input" id="query" name="query" type="text" placeholder="Query"/>
|
||||||
|
<button class="btn btn_primary form-btn" type="submit">Search
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="SearchResult">
|
||||||
|
<strong class="SearchResult-title">Found <span class="SearchResult-amount">0</span> results
|
||||||
|
</strong>
|
||||||
|
<div class="SearchResult-content">
|
||||||
|
</div>
|
||||||
|
<div class="SearchResult-footer SearchResult-footer_hide">
|
||||||
|
<button class="btn btn_primary" data-send="search" data-sendtype="next">Show more<span class="SearchResult-remain">(0)</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/assets/plg/jQuery/jquery-3.5.1.min.js"></script>
|
||||||
|
</body></html>
|