first commit

This commit is contained in:
Eduard Kuksa
2024-11-18 00:06:34 +07:00
commit 671ce3eaa0
94 changed files with 7484 additions and 0 deletions
README.mdapplication.yaml
doc
pom.xml
src/main
java
resources

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

Binary file not shown.

Binary file not shown.

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>

@@ -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);
}
}

@@ -0,0 +1,11 @@
package searchengine.config;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class Site {
private String url;
private String name;
}

@@ -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;
}

@@ -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;
}

@@ -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;
}

@@ -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;
}

@@ -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> {
}

@@ -0,0 +1,4 @@
package searchengine.services;
public interface IndexingService {
}

@@ -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;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@@ -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;
}

File diff suppressed because one or more lines are too long

After

(image error) 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;
}

File diff suppressed because one or more lines are too long

After

(image error) 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;
}

File diff suppressed because one or more lines are too long

After

(image error) 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;
}

File diff suppressed because one or more lines are too long

After

(image error) 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;
}

File diff suppressed because one or more lines are too long

After

(image error) 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;
}

File diff suppressed because one or more lines are too long

After

(image error) 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;
}

File diff suppressed because one or more lines are too long

After

(image error) 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;
}

File diff suppressed because one or more lines are too long

After

(image error) 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;
}

File diff suppressed because one or more lines are too long

After

(image error) 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

(image error) Size: 126 B

@@ -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

(image error) Size: 568 B

@@ -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

(image error) Size: 433 B

@@ -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

(image error) Size: 521 B

File diff suppressed because one or more lines are too long

After

(image error) Size: 12 KiB

@@ -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

(image error) Size: 393 B

@@ -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

(image error) Size: 1.1 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@@ -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()})}})});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@@ -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
| Ваш браузер&nbsp;
em устарел!&nbsp;
a(href="http://browsehappy.com/") Выберите новую версию
+s
| браузера здесь&nbsp;
| для правильного отображения сайта.
-->
<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&#32;<span class="SearchResult-amount">0</span>&#32;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>