Compare commits
5
Commits
daef30ec11
..
NC_YA
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ee7e439c0 | ||
|
|
503463e1d0 | ||
|
|
5f61c82dee | ||
|
|
2139ca0e8c | ||
|
|
01332c7db5 |
@@ -14,9 +14,6 @@ COPY --from=build /app/build/libs/*.jar app.jar
|
|||||||
|
|
||||||
RUN mkdir -p /data/uploads && chmod 755 /data/uploads
|
RUN mkdir -p /data/uploads && chmod 755 /data/uploads
|
||||||
|
|
||||||
ENV BUILD_TIME_BACK="unknown"\
|
|
||||||
BUILD_TIME_FRONT="unknown"
|
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
CMD ["java", "-jar", "app.jar"]
|
CMD ["java", "-jar", "app.jar"]
|
||||||
@@ -122,42 +122,3 @@ SELECT 1
|
|||||||
FROM protect_check pc
|
FROM protect_check pc
|
||||||
WHERE pc.user_id = u.id
|
WHERE pc.user_id = u.id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
------
|
|
||||||
Скрипты рефералки
|
|
||||||
|
|
||||||
INSERT INTO referrals (user_id, referral_link, inviter_id, level_id, total_income, is_active, created_at, hold_balance)
|
|
||||||
SELECT
|
|
||||||
u.id as user_id,
|
|
||||||
CONCAT('ref-', u.id, '-', LOWER(SUBSTRING(MD5(RANDOM()::text) FROM 1 FOR 8))) as referral_link,
|
|
||||||
NULL as inviter_id,
|
|
||||||
'bronze' as level_id,
|
|
||||||
0 as total_income,
|
|
||||||
false as is_active,
|
|
||||||
NOW() as created_at,
|
|
||||||
0 as hold_balance
|
|
||||||
FROM users u
|
|
||||||
LEFT JOIN referrals r ON u.id = r.user_id
|
|
||||||
WHERE
|
|
||||||
u.company_id IS NULL
|
|
||||||
AND r.user_id IS NULL
|
|
||||||
ORDER BY u.id;
|
|
||||||
|
|
||||||
-------
|
|
||||||
|
|
||||||
INSERT INTO referral_levels (id, name, min_invitees, reward_percentage) VALUES
|
|
||||||
('bronze', 'BRONZE', 0, 15),
|
|
||||||
('silver', 'SILVER', 6, 18),
|
|
||||||
('gold', 'GOLD', 16, 22),
|
|
||||||
('platinum', 'PLATINUM', 50, 25);
|
|
||||||
|
|
||||||
------
|
|
||||||
|
|
||||||
Настройка Яндекс Клауд
|
|
||||||
|
|
||||||
1. YCAJEpWAtaVkVGX0sH6_EupEg - индетификатор ключа
|
|
||||||
2. YCMmVykfXrZ_nfU13Vo4yoCVGa70DnTlBgF1pUzO - секретный ключ
|
|
||||||
|
|
||||||
private static final String KEY_ID = "YCAJEmHZcWxzf4oGWvETG3mms";
|
|
||||||
private static final String SECRET_KEY = "YCPAPZ32XC-LWb8xE0b_xTcBD8NZUCHpSOfu8LUG";
|
|
||||||
+2
-10
@@ -34,7 +34,7 @@ dependencies {
|
|||||||
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
|
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
|
||||||
implementation 'commons-validator:commons-validator:1.7'
|
implementation 'commons-validator:commons-validator:1.7'
|
||||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '4.0.1'
|
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '4.0.1'
|
||||||
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.17.2'
|
|
||||||
implementation group: 'com.google.cloud', name: 'google-cloud-vision', version: '3.55.0'
|
implementation group: 'com.google.cloud', name: 'google-cloud-vision', version: '3.55.0'
|
||||||
implementation group: 'com.google.api-client', name: 'google-api-client', version: '2.7.2'
|
implementation group: 'com.google.api-client', name: 'google-api-client', version: '2.7.2'
|
||||||
|
|
||||||
@@ -59,18 +59,10 @@ dependencies {
|
|||||||
testImplementation 'org.mockito:mockito-core:5.3.1'
|
testImplementation 'org.mockito:mockito-core:5.3.1'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
|
|
||||||
implementation name: 'testlib-fat-0.3.0-all'
|
implementation name: 'testlib-fat-0.2.1-all'
|
||||||
|
|
||||||
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.12.0'
|
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.12.0'
|
||||||
|
|
||||||
implementation project(':referral')
|
|
||||||
|
|
||||||
//cloud
|
|
||||||
implementation("software.amazon.awssdk:aws-sdk-java:2.29.33")
|
|
||||||
implementation("software.amazon.awssdk:apache-client:2.29.33")
|
|
||||||
|
|
||||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0")
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
storage:
|
||||||
|
image: alpine:latest
|
||||||
|
container_name: file-storage-local
|
||||||
|
networks:
|
||||||
|
- app-network-local
|
||||||
|
volumes:
|
||||||
|
- uploads_data_local:/storage:rw
|
||||||
|
command: tail -f /dev/null
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:17.7
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: no_copy_
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_SHARED_BUFFERS: 512MB
|
||||||
|
POSTGRES_EFFECTIVE_CACHE_SIZE: 1536MB
|
||||||
|
ports:
|
||||||
|
- "54320:5432"
|
||||||
|
volumes:
|
||||||
|
- pgdata_local:/var/lib/postgresql/data
|
||||||
|
- ./init-scripts:/docker-entrypoint-initdb.d:ro
|
||||||
|
container_name: postgres-local
|
||||||
|
networks:
|
||||||
|
app-network-local:
|
||||||
|
aliases:
|
||||||
|
- database
|
||||||
|
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
container_name: app-backend-local
|
||||||
|
environment:
|
||||||
|
JAVA_TOOL_OPTIONS: "-Xmx2g -Xms1g"
|
||||||
|
FILE_STORAGE_PATH: /data/uploads
|
||||||
|
POSTGRES_DB: no_copy_
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_PORT: 5432
|
||||||
|
POSTGRES_HOST: db
|
||||||
|
# Используем local профиль
|
||||||
|
SPRING_PROFILES_ACTIVE: local
|
||||||
|
YANDEX_API_KEY: AQVNyaVaUmgUb1GMCtf5zSEqFxy0woXrcMOOB43q
|
||||||
|
YANDEX_FOLDER_ID: b1gokpdbm6qfpsou8pcd
|
||||||
|
YANDEX_SEARCH_URL: "https://searchapi.api.cloud.yandex.net/v2/image/search_by_image"
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
networks:
|
||||||
|
app-network-local:
|
||||||
|
aliases:
|
||||||
|
- app
|
||||||
|
- backend
|
||||||
|
volumes:
|
||||||
|
- uploads_data_local:/data/uploads:rw
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "curl", "-f", "http://localhost:8080/actuator/health" ]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pgdata_local:
|
||||||
|
uploads_data_local:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app-network-local:
|
||||||
|
driver: bridge
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
storage:
|
||||||
|
image: alpine:latest
|
||||||
|
container_name: file-storage
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
volumes:
|
||||||
|
- uploads_data:/storage:rw
|
||||||
|
command: tail -f /dev/null
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
container_name: app-backend
|
||||||
|
environment:
|
||||||
|
JAVA_TOOL_OPTIONS: "-Xmx2g -Xms1g"
|
||||||
|
FILE_STORAGE_PATH: /data/uploads
|
||||||
|
# Данные для Yandex Cloud БД
|
||||||
|
YC_DB_USER: ${YC_DB_USER}
|
||||||
|
YC_DB_PASSWORD: ${YC_DB_PASSWORD}
|
||||||
|
# Используем prod профиль
|
||||||
|
SPRING_PROFILES_ACTIVE: prod
|
||||||
|
YANDEX_API_KEY: AQVNyaVaUmgUb1GMCtf5zSEqFxy0woXrcMOOB43q
|
||||||
|
YANDEX_FOLDER_ID: b1gokpdbm6qfpsou8pcd
|
||||||
|
YANDEX_SEARCH_URL: "https://searchapi.api.cloud.yandex.net/v2/image/search_by_image"
|
||||||
|
ports:
|
||||||
|
- "80:8080"
|
||||||
|
networks:
|
||||||
|
app-network:
|
||||||
|
aliases:
|
||||||
|
- app
|
||||||
|
- backend
|
||||||
|
volumes:
|
||||||
|
- uploads_data:/data/uploads:rw
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "curl", "-f", "http://localhost:8080/actuator/health" ]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
uploads_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app-network:
|
||||||
|
external: true
|
||||||
|
driver: bridge
|
||||||
+1
-1
@@ -181,7 +181,7 @@ services:
|
|||||||
command:
|
command:
|
||||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||||
- '--storage.tsdb.path=/prometheus'
|
- '--storage.tsdb.path=/prometheus'
|
||||||
- '--storage.tsdb.retention.time=15d'
|
- '--storage.tsdb.retention.time=15d' # Удерживать 15 дней
|
||||||
- '--web.enable-lifecycle'
|
- '--web.enable-lifecycle'
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: [ "CMD", "wget", "--spider", "http://localhost:9090/-/healthy" ]
|
test: [ "CMD", "wget", "--spider", "http://localhost:9090/-/healthy" ]
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ pipeline {
|
|||||||
stage('Deploy with docker-compose') {
|
stage('Deploy with docker-compose') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
def buildTime = sh(script: "date '+%d-%m-%Y %H:%M:%S'", returnStdout: true).trim()
|
|
||||||
withCredentials([
|
withCredentials([
|
||||||
usernamePassword(
|
usernamePassword(
|
||||||
credentialsId: 'server-root-password',
|
credentialsId: 'server-root-password',
|
||||||
@@ -86,7 +85,6 @@ pipeline {
|
|||||||
-e POSTGRES_USER=${DB_USER} \\
|
-e POSTGRES_USER=${DB_USER} \\
|
||||||
-e POSTGRES_PASSWORD=${DB_PASSWORD} \\
|
-e POSTGRES_PASSWORD=${DB_PASSWORD} \\
|
||||||
-v pgdata_${params.PORT}:/var/lib/postgresql/data \\
|
-v pgdata_${params.PORT}:/var/lib/postgresql/data \\
|
||||||
-e BUILD_TIME_BACK='${buildTime}' \\
|
|
||||||
postgres:17.7
|
postgres:17.7
|
||||||
|
|
||||||
echo '4. Запускаем storage...'
|
echo '4. Запускаем storage...'
|
||||||
|
|||||||
@@ -85,8 +85,6 @@ pipeline {
|
|||||||
stage('Build on server') {
|
stage('Build on server') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
def BUILD_TIME_FRONT = sh(script: "date '+%d-%m-%Y %H:%M:%S'", returnStdout: true).trim()
|
|
||||||
|
|
||||||
withCredentials([
|
withCredentials([
|
||||||
usernamePassword(
|
usernamePassword(
|
||||||
credentialsId: 'server-root-password',
|
credentialsId: 'server-root-password',
|
||||||
@@ -95,20 +93,23 @@ pipeline {
|
|||||||
)
|
)
|
||||||
]) {
|
]) {
|
||||||
sh """
|
sh """
|
||||||
sshpass -p '$SSH_PASS' ssh $SSH_USER@92.242.61.23 "
|
sshpass -p '$SSH_PASS' ssh $SSH_USER@92.242.61.23 "
|
||||||
cd /opt/deployments/frontend/${params.BRANCH}-${params.PORT}
|
cd /opt/deployments/frontend/${params.BRANCH}-${params.PORT}
|
||||||
|
|
||||||
# 1. СОЗДАЕМ JSON ФАЙЛ (ЭТО РАБОТАЕТ 100%)
|
echo '=== Редактируем docker-compose.yml ==='
|
||||||
mkdir -p public
|
|
||||||
echo '{\\"buildTime\\": \\"${BUILD_TIME_FRONT}\\"}' > public/build-info.json
|
|
||||||
|
|
||||||
# 2. Меняем имя контейнера и порт
|
# Меняем имя контейнера
|
||||||
sed -i 's/container_name: no-copy-frontend\$/container_name: no-copy-frontend-${params.PORT}/' docker-compose.yml
|
sed -i 's/container_name: no-copy-frontend\$/container_name: no-copy-frontend-${params.PORT}/' docker-compose.yml
|
||||||
sed -i 's/\\\"2998:2999\\\"/\\\"${params.PORT}:2999\\\"/' docker-compose.yml
|
|
||||||
|
|
||||||
# 3. Собираем образ
|
# Меняем порт (2998 на нужный порт)
|
||||||
docker-compose -f docker-compose.yml -p frontend-${params.PORT} build
|
sed -i 's/\\\"2998:2999\\\"/\\\"${params.PORT}:2999\\\"/' docker-compose.yml
|
||||||
"
|
|
||||||
|
echo 'Проверяем изменения:'
|
||||||
|
grep -n 'container_name\\|ports' docker-compose.yml
|
||||||
|
|
||||||
|
echo '=== Строим образ ==='
|
||||||
|
docker-compose -f docker-compose.yml -p frontend-${params.PORT} build
|
||||||
|
"
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ pipeline {
|
|||||||
stage('Deploy with docker-compose') {
|
stage('Deploy with docker-compose') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
def buildTime = sh(script: "date '+%d-%m-%Y %H:%M:%S'", returnStdout: true).trim()
|
|
||||||
withCredentials([
|
withCredentials([
|
||||||
usernamePassword(
|
usernamePassword(
|
||||||
credentialsId: 'server-root-password',
|
credentialsId: 'server-root-password',
|
||||||
@@ -93,7 +92,6 @@ pipeline {
|
|||||||
-e POSTGRES_PASSWORD=$DB_PASSWORD \\
|
-e POSTGRES_PASSWORD=$DB_PASSWORD \\
|
||||||
-e POSTGRES_PORT=5432 \\
|
-e POSTGRES_PORT=5432 \\
|
||||||
-e POSTGRES_HOST=db \\
|
-e POSTGRES_HOST=db \\
|
||||||
-e BUILD_TIME_BACK='${buildTime}' \\
|
|
||||||
--restart unless-stopped \\
|
--restart unless-stopped \\
|
||||||
app-backend:latest
|
app-backend:latest
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -2,9 +2,9 @@ package ru.soune
|
|||||||
|
|
||||||
interface Referral {
|
interface Referral {
|
||||||
|
|
||||||
val userId: Long
|
val userId: String
|
||||||
val referralLink: String
|
val referralLink: String
|
||||||
val inviter: Long?
|
val inviter: String?
|
||||||
val currentLevel: String
|
val currentLevel: String
|
||||||
val totalIncome: Int
|
val totalIncome: Int
|
||||||
val availableIncome: Int
|
val availableIncome: Int
|
||||||
|
|||||||
@@ -2,32 +2,32 @@ package ru.soune
|
|||||||
|
|
||||||
interface ReferralRepo {
|
interface ReferralRepo {
|
||||||
|
|
||||||
fun getUserReferralLink(userId: Long): String
|
fun getUserReferralLink(userId: String): String
|
||||||
|
|
||||||
fun getUserIdByLink(link: String): Long?
|
fun getUserIdByLink(link: String): String
|
||||||
|
|
||||||
fun getInviterIdForUser(userId: Long): Long?
|
fun getInviterIdForUser(userId: String): String?
|
||||||
|
|
||||||
fun getReferralLevelForUser(userId: Long): String
|
fun getReferralLevelForUser(userId: String): String
|
||||||
|
|
||||||
fun getReferralByUserId(userId: Long): Referral
|
fun getReferralByUserId(userId: String): Referral
|
||||||
|
|
||||||
fun getReferralInvitees(userId: Long, pageSize: Int, pageNumber: Int): List<ReferralInvitee>
|
fun getReferralInvitees(userId: String, pageSize: Int, pageNumber: Int): List<ReferralInvitee>
|
||||||
|
|
||||||
// создаем новую запись в БД
|
// создаем новую запись в БД
|
||||||
fun createReferralEntity(entity: Referral)
|
fun createReferralEntity(entity: Referral)
|
||||||
|
|
||||||
// прибавить к totalIncome и availableIncome значение transferAmount
|
// прибавить к totalIncome и availableIncome значение transferAmount
|
||||||
fun increaseIncome(userId: Long, transferAmount: Int)
|
fun increaseIncome(userId: String, transferAmount: Int)
|
||||||
|
|
||||||
// установить active в true
|
// установить active в true
|
||||||
fun activateUser(userId: Long): Boolean
|
fun activateUser(userId: String): Boolean
|
||||||
|
|
||||||
// посчитать записи, у которых inviter == userId и active == true
|
// посчитать записи, у которых inviter == userId и active == true
|
||||||
fun getActiveInviteeForUser(userId: Long): Int
|
fun getActiveInviteeForUser(userId: String): Int
|
||||||
|
|
||||||
// посчитать записи, у которых inviter == userId
|
// посчитать записи, у которых inviter == userId
|
||||||
fun getTotalInviteeForUser(userId: Long): Int
|
fun getTotalInviteeForUser(userId: String): Int
|
||||||
|
|
||||||
fun upgradeReferralLevelForUser(userId: Long, newLevelId: String)
|
fun upgradeReferralLevelForUser(userId: String, newLevelId: String)
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ class ReferralService(
|
|||||||
private val referralRepo: ReferralRepo,
|
private val referralRepo: ReferralRepo,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun onRegister(userId: Long, inviterReferralLink: String?) {
|
fun onRegister(userId: String, inviterReferralLink: String?) {
|
||||||
val initialReferralLevel = referralLevelProvider.getInitialReferralLevelId()
|
val initialReferralLevel = referralLevelProvider.getInitialReferralLevelId()
|
||||||
val referralLink = referralRepo.getUserReferralLink(userId)
|
val referralLink = referralRepo.getUserReferralLink(userId)
|
||||||
val inviter = inviterReferralLink?.let(referralRepo::getUserIdByLink)
|
val inviter = inviterReferralLink?.let(referralRepo::getUserIdByLink)
|
||||||
@@ -24,7 +24,7 @@ class ReferralService(
|
|||||||
referralRepo.createReferralEntity(referral)
|
referralRepo.createReferralEntity(referral)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onUserAccountRefill(userId: Long, transferAmount: Int) {
|
fun onUserAccountRefill(userId: String, transferAmount: Int) {
|
||||||
val wasActivated = referralRepo.activateUser(userId)
|
val wasActivated = referralRepo.activateUser(userId)
|
||||||
val inviterId = referralRepo.getInviterIdForUser(userId) ?: return
|
val inviterId = referralRepo.getInviterIdForUser(userId) ?: return
|
||||||
val currentInviterLevel = referralRepo.getReferralLevelForUser(inviterId)
|
val currentInviterLevel = referralRepo.getReferralLevelForUser(inviterId)
|
||||||
@@ -47,11 +47,11 @@ class ReferralService(
|
|||||||
referralLevelProvider.getAvailableReferralLevels()
|
referralLevelProvider.getAvailableReferralLevels()
|
||||||
|
|
||||||
|
|
||||||
fun getReferralLevelForUser(userId: Long): ReferralLevel =
|
fun getReferralLevelForUser(userId: String): ReferralLevel =
|
||||||
referralRepo.getReferralLevelForUser(userId)
|
referralRepo.getReferralLevelForUser(userId)
|
||||||
.let { referralLevelProvider.getReferralLevelById(it) }
|
.let { referralLevelProvider.getReferralLevelById(it) }
|
||||||
|
|
||||||
fun getReferralStatForUser(userId: Long): ReferralStat {
|
fun getReferralStatForUser(userId: String): ReferralStat {
|
||||||
var referral = referralRepo.getReferralByUserId(userId)
|
var referral = referralRepo.getReferralByUserId(userId)
|
||||||
return ReferralStat(
|
return ReferralStat(
|
||||||
totalInvitee = referralRepo.getTotalInviteeForUser(userId),
|
totalInvitee = referralRepo.getTotalInviteeForUser(userId),
|
||||||
@@ -59,10 +59,9 @@ class ReferralService(
|
|||||||
totalIncome = referral.totalIncome,
|
totalIncome = referral.totalIncome,
|
||||||
availableIncome = referral.availableIncome,
|
availableIncome = referral.availableIncome,
|
||||||
holdBalance = referral.holdBalance,
|
holdBalance = referral.holdBalance,
|
||||||
referralLink = referral.referralLink
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInviteeForUser(userId: Long, pageSize: Int, pageNumber: Int): List<ReferralInvitee> =
|
fun getInviteeForUser(userId: String, pageSize: Int, pageNumber: Int): List<ReferralInvitee> =
|
||||||
referralRepo.getReferralInvitees(userId, pageSize, pageNumber)
|
referralRepo.getReferralInvitees(userId, pageSize, pageNumber)
|
||||||
}
|
}
|
||||||
@@ -6,5 +6,4 @@ data class ReferralStat(
|
|||||||
val totalIncome: Int,
|
val totalIncome: Int,
|
||||||
val availableIncome: Int,
|
val availableIncome: Int,
|
||||||
val holdBalance: Int,
|
val holdBalance: Int,
|
||||||
val referralLink: String,
|
|
||||||
)
|
)
|
||||||
@@ -3,9 +3,9 @@ package ru.soune.nocopy.configuration;
|
|||||||
import com.vrt.AudioFilePathProvider;
|
import com.vrt.AudioFilePathProvider;
|
||||||
import com.vrt.fileprotection.FileProtector;
|
import com.vrt.fileprotection.FileProtector;
|
||||||
import com.vrt.fileprotection.audio.AudioLocalSearch;
|
import com.vrt.fileprotection.audio.AudioLocalSearch;
|
||||||
import com.vrt.fileprotection.documents.DocumentLocalSearch;
|
|
||||||
import com.vrt.fileprotection.image.ImageLocalSearch;
|
import com.vrt.fileprotection.image.ImageLocalSearch;
|
||||||
import com.vrt.fileprotection.image.ImageUniqueCheck;
|
import com.vrt.fileprotection.image.ImageUniqueCheck;
|
||||||
|
import com.vrt.fileprotection.image.phash.PerceptualHashHelper;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -33,8 +33,7 @@ public class ApplicationConfig {
|
|||||||
ImageUniqueCheck imageUniqueCheck,
|
ImageUniqueCheck imageUniqueCheck,
|
||||||
ImageLocalSearch imageLocalSearch,
|
ImageLocalSearch imageLocalSearch,
|
||||||
AudioLocalSearch audioLocalSearch,
|
AudioLocalSearch audioLocalSearch,
|
||||||
AudioFilePathProvider audioFilePathProvider,
|
AudioFilePathProvider audioFilePathProvider) {
|
||||||
DocumentLocalSearch documentLocalSearch) {
|
|
||||||
|
|
||||||
return new com.vrt.NoCopyFileService(
|
return new com.vrt.NoCopyFileService(
|
||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
@@ -43,8 +42,7 @@ public class ApplicationConfig {
|
|||||||
imageUniqueCheck,
|
imageUniqueCheck,
|
||||||
imageLocalSearch,
|
imageLocalSearch,
|
||||||
audioLocalSearch,
|
audioLocalSearch,
|
||||||
audioFilePathProvider,
|
audioFilePathProvider
|
||||||
documentLocalSearch
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ public class HandlerConfig {
|
|||||||
AuthRequestHandler authRequestHandler,
|
AuthRequestHandler authRequestHandler,
|
||||||
CompanyHandler companyHandler,
|
CompanyHandler companyHandler,
|
||||||
TariffHandler tariffHandler,
|
TariffHandler tariffHandler,
|
||||||
TariffInfoHandler tariffInfoHandler,
|
TariffInfoHandler tariffInfoHandler
|
||||||
ReferralHandler referralHandler,
|
|
||||||
DaDataHandler daDataHandler
|
|
||||||
) {
|
) {
|
||||||
Map<Integer, RequestHandler> map = new HashMap<>();
|
Map<Integer, RequestHandler> map = new HashMap<>();
|
||||||
map.put(20001, login);
|
map.put(20001, login);
|
||||||
@@ -38,8 +36,6 @@ public class HandlerConfig {
|
|||||||
map.put(30000, companyHandler);
|
map.put(30000, companyHandler);
|
||||||
map.put(30001, tariffHandler);
|
map.put(30001, tariffHandler);
|
||||||
map.put(30002, tariffInfoHandler);
|
map.put(30002, tariffInfoHandler);
|
||||||
map.put(30003, referralHandler);
|
|
||||||
map.put(30004, daDataHandler);
|
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -21,13 +20,7 @@ public class JacksonConfig {
|
|||||||
@Bean
|
@Bean
|
||||||
@Primary
|
@Primary
|
||||||
public ObjectMapper objectMapper() {
|
public ObjectMapper objectMapper() {
|
||||||
ObjectMapper mapper = Jackson2ObjectMapperBuilder.json()
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
.defaultViewInclusion(true)
|
|
||||||
.autoDetectFields(true)
|
|
||||||
.failOnUnknownProperties(false)
|
|
||||||
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
|
||||||
.featuresToEnable(SerializationFeature.INDENT_OUTPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package ru.soune.nocopy.configuration;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import ru.soune.ReferralLevelProvider;
|
|
||||||
import ru.soune.ReferralRepo;
|
|
||||||
import ru.soune.ReferralService;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class ReferralConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ReferralService referralService(ReferralLevelProvider referralLevelProvider, ReferralRepo referralRepo) {
|
|
||||||
return new ReferralService(referralLevelProvider, referralRepo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,7 +32,6 @@ import ru.soune.nocopy.repository.AuthTokenRepository;
|
|||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||||
import ru.soune.nocopy.service.FileSimilarityService;
|
import ru.soune.nocopy.service.FileSimilarityService;
|
||||||
import ru.soune.nocopy.service.file.ProtectionsLimitService;
|
import ru.soune.nocopy.service.file.ProtectionsLimitService;
|
||||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
|
||||||
import ru.soune.nocopy.service.register.AuthService;
|
import ru.soune.nocopy.service.register.AuthService;
|
||||||
import ru.soune.nocopy.service.file.FileEntityService;
|
import ru.soune.nocopy.service.file.FileEntityService;
|
||||||
import ru.soune.nocopy.service.file.FileUploadService;
|
import ru.soune.nocopy.service.file.FileUploadService;
|
||||||
@@ -74,8 +73,6 @@ public class ApiController {
|
|||||||
|
|
||||||
private final AuthTokenRepository authTokenRepository;
|
private final AuthTokenRepository authTokenRepository;
|
||||||
|
|
||||||
private final CloudStorageService cloudStorageService;
|
|
||||||
|
|
||||||
@PostMapping("/v{version}/data")
|
@PostMapping("/v{version}/data")
|
||||||
public ResponseEntity<?> handlePostRequest(@RequestBody BaseRequest request,
|
public ResponseEntity<?> handlePostRequest(@RequestBody BaseRequest request,
|
||||||
@PathVariable("version") int version) {
|
@PathVariable("version") int version) {
|
||||||
@@ -345,14 +342,15 @@ public class ApiController {
|
|||||||
errorData));
|
errorData));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path filePath = Paths.get(entityResponse.getProtectedFilePath());
|
Path filePath = Paths.get(entityResponse.getProtectedFilePath());
|
||||||
File file = cloudStorageService.readFileFromStorageByPath(
|
// Resource resource = new UrlResource(filePath.toUri());
|
||||||
entityResponse.getProtectedFilePath());
|
FileSystemResource resource = new FileSystemResource(filePath);
|
||||||
long fileSize = Files.size(filePath);
|
long fileSize = Files.size(filePath);
|
||||||
|
|
||||||
if (!file.exists()) {
|
if (!resource.exists()) {
|
||||||
Map<String, Object> errorData = new HashMap<>();
|
Map<String, Object> errorData = new HashMap<>();
|
||||||
errorData.put("file", file.exists());
|
errorData.put("resource", resource.exists());
|
||||||
|
|
||||||
return ResponseEntity.ok().body(new BaseResponse(20004,
|
return ResponseEntity.ok().body(new BaseResponse(20004,
|
||||||
MessageCode.FILE_DOWNLOAD_ERROR.getCode(),
|
MessageCode.FILE_DOWNLOAD_ERROR.getCode(),
|
||||||
@@ -366,8 +364,8 @@ public class ApiController {
|
|||||||
.contentLength(fileSize)
|
.contentLength(fileSize)
|
||||||
.contentType(MediaType.parseMediaType(contentType))
|
.contentType(MediaType.parseMediaType(contentType))
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||||
"attachment; filename=\"" + entityResponse.getFileName() + "\"")
|
"attachment; filename=\"" + entityResponse.getOriginalFileName() + "\"")
|
||||||
.body(file);
|
.body(resource);
|
||||||
} catch (FileEntityNotFoundException e) {
|
} catch (FileEntityNotFoundException e) {
|
||||||
Map<String, Object> errorData = new HashMap<>();
|
Map<String, Object> errorData = new HashMap<>();
|
||||||
errorData.put("fileId", fileId);
|
errorData.put("fileId", fileId);
|
||||||
@@ -441,6 +439,33 @@ public class ApiController {
|
|||||||
return ResponseEntity.ok().body(fileTypeStats);
|
return ResponseEntity.ok().body(fileTypeStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasDuplicate(List<SimilarFileDTO> similarFiles) {
|
||||||
|
return similarFiles.stream().anyMatch(f -> f.getHammingDistance() <= 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<BaseResponse> handleDuplicate(FileEntity fileEntity, List<SimilarFileDTO> similarFiles)
|
||||||
|
throws IOException {
|
||||||
|
fileEntityService.deleteFromDisk(fileEntity);
|
||||||
|
|
||||||
|
fileEntityService.softDeleteFileWithHash(fileEntity);
|
||||||
|
|
||||||
|
Optional<FileEntity> originalFile = fileEntityRepository.findById(similarFiles.get(0).getFileId());
|
||||||
|
|
||||||
|
if (originalFile.isPresent()) {
|
||||||
|
Map<String, String> duplicateInfo = Map.of(
|
||||||
|
"duplicate_file_id", originalFile.get().getId(),
|
||||||
|
"owner_user_id", String.valueOf(originalFile.get().getUserId()));
|
||||||
|
|
||||||
|
return ResponseEntity.ok().body(new BaseResponse(
|
||||||
|
20004,
|
||||||
|
MessageCode.DUPLICATE_FILE_UPLOAD.getCode(),
|
||||||
|
"Failed to upload chunk, duplicate",
|
||||||
|
duplicateInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private ResponseEntity<BaseResponse> buildSuccessResponse(String uploadId, Integer chunkNumber, MultipartFile chunk,
|
private ResponseEntity<BaseResponse> buildSuccessResponse(String uploadId, Integer chunkNumber, MultipartFile chunk,
|
||||||
String fileId) {
|
String fileId) {
|
||||||
ChunkUploadResponse responseBody = ChunkUploadResponse.builder()
|
ChunkUploadResponse responseBody = ChunkUploadResponse.builder()
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import ru.soune.nocopy.repository.FileEntityRepository;
|
|||||||
import ru.soune.nocopy.repository.UserRepository;
|
import ru.soune.nocopy.repository.UserRepository;
|
||||||
import ru.soune.nocopy.service.file.CheckCounterService;
|
import ru.soune.nocopy.service.file.CheckCounterService;
|
||||||
import ru.soune.nocopy.service.file.FileStorageService;
|
import ru.soune.nocopy.service.file.FileStorageService;
|
||||||
import org.springframework.http.ContentDisposition;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@@ -47,13 +45,10 @@ public class FileController {
|
|||||||
|
|
||||||
Resource resource = fileStorageService.loadFileAsResource(fileEntity.getFilePath());
|
Resource resource = fileStorageService.loadFileAsResource(fileEntity.getFilePath());
|
||||||
|
|
||||||
ContentDisposition contentDisposition = ContentDisposition.inline()
|
|
||||||
.filename(fileEntity.getOriginalFileName(), StandardCharsets.UTF_8)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.contentType(new MediaType(fileEntity.getMimeType(), fileEntity.getFileExtension()))
|
.contentType(new MediaType(fileEntity.getMimeType(), fileEntity.getFileExtension()))
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString())
|
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||||
|
"inline; filename=\"" + fileEntity.getOriginalFileName() + "\"")
|
||||||
.body(resource);
|
.body(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package ru.soune.nocopy.controller;
|
package ru.soune.nocopy.controller;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -16,14 +15,6 @@ public class HealtCheckController {
|
|||||||
return HttpStatus.OK;
|
return HttpStatus.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Value("${BUILD_TIME_BACK:unknown}")
|
|
||||||
private String buildTimeBack;
|
|
||||||
|
|
||||||
@GetMapping("/build")
|
|
||||||
public BuildInfo getBuildInfo() {
|
|
||||||
return new BuildInfo(buildTimeBack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/api/debug/memory")
|
@GetMapping("/api/debug/memory")
|
||||||
public Map<String, String> getMemoryInfo() {
|
public Map<String, String> getMemoryInfo() {
|
||||||
Runtime runtime = Runtime.getRuntime();
|
Runtime runtime = Runtime.getRuntime();
|
||||||
@@ -41,12 +32,4 @@ public class HealtCheckController {
|
|||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class BuildInfo {
|
|
||||||
public String buildTimeBack;
|
|
||||||
|
|
||||||
public BuildInfo(String buildTimeBack) {
|
|
||||||
this.buildTimeBack = buildTimeBack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (user.canManageCompanySettings() || user.canLogin() && user.getCompany() == null) {
|
if (user.canManageCompanySettings() || user.canLogin() && user.getCompany() == null) {
|
||||||
userDTO.setTariffs(tariffService.getTariffByAccountType(user));
|
userDTO.setTariffs(tariffService.getAllTariffs());
|
||||||
|
|
||||||
Long fileOnDisk = user.canManageCompanySettings() ?
|
Long fileOnDisk = user.canManageCompanySettings() ?
|
||||||
fileStatsService.calculateCompanyFileSize(user.getId()):
|
fileStatsService.calculateCompanyFileSize(user.getId()):
|
||||||
@@ -116,7 +116,6 @@ public class UserController {
|
|||||||
.maxFileOnDisk(tariff.getDiskSize())
|
.maxFileOnDisk(tariff.getDiskSize())
|
||||||
.startTariff(personalTariffInfo.getStartTariff())
|
.startTariff(personalTariffInfo.getStartTariff())
|
||||||
.endTariff(personalTariffInfo.getEndTariff())
|
.endTariff(personalTariffInfo.getEndTariff())
|
||||||
.tokens(personalTariffInfo.getTokens() + personalTariffInfo.getBoughtTokens())
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
userDTO.setTariffInfo(infoDTO);
|
userDTO.setTariffInfo(infoDTO);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ public enum MessageCode {
|
|||||||
SUCCESS(0, "Operation successful"),
|
SUCCESS(0, "Operation successful"),
|
||||||
REG_EMAIL_EXISTS(1, "Email already registered"),
|
REG_EMAIL_EXISTS(1, "Email already registered"),
|
||||||
REG_EMAIL_OR_PHONE_EXISTS(1, "Email or phone already registered"),
|
REG_EMAIL_OR_PHONE_EXISTS(1, "Email or phone already registered"),
|
||||||
REFERRAL_LINK_IS_NOT_EXIST(1, "Refferal link is not exist"),
|
|
||||||
INVALID_FIELD(2, "Invalid field"),
|
INVALID_FIELD(2, "Invalid field"),
|
||||||
INVALID_TOKEN(2, "Invalid token"),
|
INVALID_TOKEN(2, "Invalid token"),
|
||||||
TOKEN_IS_ALIVE(2, "Token is alive"),
|
TOKEN_IS_ALIVE(2, "Token is alive"),
|
||||||
@@ -41,7 +40,6 @@ public enum MessageCode {
|
|||||||
INTERNAL_ERROR(4, "Internal server error"),
|
INTERNAL_ERROR(4, "Internal server error"),
|
||||||
COMPANY_NOT_FOUND(4, "Company not found"),
|
COMPANY_NOT_FOUND(4, "Company not found"),
|
||||||
COMPANY_ALREADY_EXISTS(2, "Company already exists"),
|
COMPANY_ALREADY_EXISTS(2, "Company already exists"),
|
||||||
USER_LIMIT_IS_OVER(2, "Over user limits"),
|
|
||||||
ERROR_TARIFF_INFO(2, "Erorr with tariff info");
|
ERROR_TARIFF_INFO(2, "Erorr with tariff info");
|
||||||
|
|
||||||
private final Integer code;
|
private final Integer code;
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package ru.soune.nocopy.dto.dadata;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public class DaDataAddress {
|
|
||||||
private String value;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package ru.soune.nocopy.dto.dadata;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public class DaDataData {
|
|
||||||
private String inn;
|
|
||||||
private DaDataAddress address;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package ru.soune.nocopy.dto.dadata;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class DaDataRequest {
|
|
||||||
|
|
||||||
@JsonProperty("inn")
|
|
||||||
private String inn;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package ru.soune.nocopy.dto.dadata;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class DaDataResponse {
|
|
||||||
@JsonProperty("companyName")
|
|
||||||
private String companyName;
|
|
||||||
|
|
||||||
@JsonProperty("inn")
|
|
||||||
private String inn;
|
|
||||||
|
|
||||||
@JsonProperty("address")
|
|
||||||
private String address;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package ru.soune.nocopy.dto.dadata;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public class DaDataSuggestion {
|
|
||||||
private String value;
|
|
||||||
private DaDataData data;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package ru.soune.nocopy.dto.dadata;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public class DaDataWrapper {
|
|
||||||
private List<DaDataSuggestion> suggestions;
|
|
||||||
}
|
|
||||||
@@ -59,21 +59,6 @@ public class FileInfoUserResponse {
|
|||||||
@JsonProperty("audios_violations")
|
@JsonProperty("audios_violations")
|
||||||
private Integer audiosViolations;
|
private Integer audiosViolations;
|
||||||
|
|
||||||
@JsonProperty("document_size")
|
|
||||||
private Long documentSize;
|
|
||||||
|
|
||||||
@JsonProperty("document_quantity")
|
|
||||||
private Integer documentCount;
|
|
||||||
|
|
||||||
@JsonProperty("document_check")
|
|
||||||
private Integer documentCheck;
|
|
||||||
|
|
||||||
@JsonProperty("document_violations")
|
|
||||||
private Integer documentViolations;
|
|
||||||
|
|
||||||
@JsonProperty("protected_document_files_count")
|
|
||||||
private Long protectedDocumentFilesCount;
|
|
||||||
|
|
||||||
@JsonProperty("protected_files_count")
|
@JsonProperty("protected_files_count")
|
||||||
private Long protectedFilesCount;
|
private Long protectedFilesCount;
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,4 @@ import lombok.Data;
|
|||||||
public class ImageSearchRequest {
|
public class ImageSearchRequest {
|
||||||
@JsonProperty("file_id")
|
@JsonProperty("file_id")
|
||||||
private String fileId;
|
private String fileId;
|
||||||
|
|
||||||
@JsonProperty("page")
|
|
||||||
private Integer page = 1;
|
|
||||||
|
|
||||||
@JsonProperty("page_size")
|
|
||||||
private Integer pageSize = 10;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,14 @@ public class YandexSearchResponse {
|
|||||||
@JsonProperty("images")
|
@JsonProperty("images")
|
||||||
private List<ImageResult> images;
|
private List<ImageResult> images;
|
||||||
|
|
||||||
// @JsonProperty("images")
|
@JsonProperty("images")
|
||||||
// public void setImages(List<ImageResult> images) {
|
public void setImages(List<ImageResult> images) {
|
||||||
// this.images = images;
|
if (images != null && images.size() > 5) {
|
||||||
// }
|
this.images = images.subList(0, 5);
|
||||||
|
} else {
|
||||||
|
this.images = images;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
@@ -38,12 +42,4 @@ public class YandexSearchResponse {
|
|||||||
@JsonProperty("host")
|
@JsonProperty("host")
|
||||||
private String host;
|
private String host;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int page;
|
|
||||||
|
|
||||||
private int pageSize;
|
|
||||||
|
|
||||||
private int totalResults;
|
|
||||||
|
|
||||||
private int totalPages;
|
|
||||||
}
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package ru.soune.nocopy.dto.referral;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
@Builder
|
|
||||||
public class ReferralRequest {
|
|
||||||
private String token;
|
|
||||||
private Integer amount;
|
|
||||||
private Integer pageSize;
|
|
||||||
private Integer pageNumber;
|
|
||||||
private String action;
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,4 @@ public class RegRequest {
|
|||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
private String authToken;
|
private String authToken;
|
||||||
|
|
||||||
private String referralLink;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,4 @@ public class TariffRequest {
|
|||||||
|
|
||||||
@JsonProperty("action")
|
@JsonProperty("action")
|
||||||
private String action;
|
private String action;
|
||||||
|
|
||||||
@JsonProperty("user_token")
|
|
||||||
private String userToken;
|
|
||||||
}
|
}
|
||||||
@@ -7,12 +7,11 @@ import java.util.List;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public enum FileType {
|
public enum FileType {
|
||||||
|
// IMAGE("image", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp", "jfif")),
|
||||||
IMAGE("image", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp")),
|
IMAGE("image", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp")),
|
||||||
VIDEO("video", Arrays.asList("mp4", "avi", "mov", "wmv", "flv", "mkv", "webm", "m4v", "mpg", "mpeg",
|
VIDEO("video", Arrays.asList("mp4", "avi", "mov", "wmv", "flv", "mkv", "webm", "m4v", "mpg", "mpeg",
|
||||||
"3gp", "3g2", "f4v", "m2ts", "mts", "vob", "ogv", "divx")),
|
"3gp", "3g2", "f4v", "m2ts", "mts", "vob", "ogv", "divx")),
|
||||||
AUDIO("audio", List.of("wav")),
|
AUDIO("audio", List.of("wav"));
|
||||||
DOCUMENT("document", Arrays.asList("pdf"));
|
|
||||||
|
|
||||||
|
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
private final List<String> allowedExtensions;
|
private final List<String> allowedExtensions;
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package ru.soune.nocopy.entity.referral;
|
|
||||||
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.data.annotation.CreatedDate;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "referrals")
|
|
||||||
@Data
|
|
||||||
public class Referral {
|
|
||||||
@Id
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
@Column(name = "referral_link", unique = true, nullable = false)
|
|
||||||
private String referralLink;
|
|
||||||
|
|
||||||
@Column(name = "inviter_id")
|
|
||||||
private Long inviterId = 0L;
|
|
||||||
|
|
||||||
@Column(name = "level_id")
|
|
||||||
private String levelId = "bronze";
|
|
||||||
|
|
||||||
@Column(name = "total_income")
|
|
||||||
private int totalIncome = 0;
|
|
||||||
|
|
||||||
@Column(name = "is_active")
|
|
||||||
private boolean isActive = false;
|
|
||||||
|
|
||||||
@CreatedDate
|
|
||||||
@Column(name = "created_at", updatable = false, nullable = false)
|
|
||||||
private LocalDateTime createdAt = LocalDateTime.now();
|
|
||||||
|
|
||||||
@Column(name = "hold_balance")
|
|
||||||
private int holdBalance = 0;
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package ru.soune.nocopy.entity.referral;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "referral_levels")
|
|
||||||
@Data
|
|
||||||
public class ReferralLevelEntity {
|
|
||||||
@Id
|
|
||||||
private String id;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Column(name = "min_invitees")
|
|
||||||
private int minInvitees;
|
|
||||||
|
|
||||||
@Column(name = "reward_percentage")
|
|
||||||
private int rewardPercentage;
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,4 @@ public class Tariff {
|
|||||||
|
|
||||||
@Column(name = "max_users")
|
@Column(name = "max_users")
|
||||||
private Long maxUsers;
|
private Long maxUsers;
|
||||||
|
|
||||||
@Column(name = "tariffAccountType")
|
|
||||||
private String tariffAccountType;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ public class TariffInfo {
|
|||||||
@Column(name = "tokens")
|
@Column(name = "tokens")
|
||||||
private Integer tokens;
|
private Integer tokens;
|
||||||
|
|
||||||
@Column(name = "bought_tokens")
|
|
||||||
private Integer boughtTokens = 0;
|
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "tariff_id")
|
@JoinColumn(name = "tariff_id")
|
||||||
private Tariff tariff;
|
private Tariff tariff;
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ package ru.soune.nocopy.entity.tarif;
|
|||||||
|
|
||||||
|
|
||||||
public enum TariffType {
|
public enum TariffType {
|
||||||
START, BASIC, PRO, ENTERPRISE, DEMO, BUISNES, CORPORAT, STUDIO, FREE;
|
START, BASIC, PRO, ENTERPRISE, DEMO;
|
||||||
}
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package ru.soune.nocopy.exception;
|
|
||||||
|
|
||||||
public class MaxUserCountIsOver extends RuntimeException {
|
|
||||||
public MaxUserCountIsOver(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package ru.soune.nocopy.handler;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import ru.soune.nocopy.dto.BaseRequest;
|
|
||||||
import ru.soune.nocopy.dto.BaseResponse;
|
|
||||||
import ru.soune.nocopy.dto.MessageCode;
|
|
||||||
import ru.soune.nocopy.dto.dadata.DaDataRequest;
|
|
||||||
import ru.soune.nocopy.dto.dadata.DaDataResponse;
|
|
||||||
import ru.soune.nocopy.service.dadata.DaDataService;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Slf4j
|
|
||||||
public class DaDataHandler implements RequestHandler {
|
|
||||||
|
|
||||||
private final DaDataService daDataService;
|
|
||||||
|
|
||||||
private ObjectMapper mapper = new ObjectMapper();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseResponse handle(BaseRequest request) throws Exception {
|
|
||||||
DaDataRequest daDataRequest = mapper.convertValue(request.getMessageBody(), DaDataRequest.class);
|
|
||||||
String inn = daDataRequest.getInn();
|
|
||||||
DaDataResponse daData = daDataService.callDaData(inn);
|
|
||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
|
||||||
MessageCode.SUCCESS.getDescription(), daData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,6 @@ import ru.soune.nocopy.entity.file.ProtectionStatus;
|
|||||||
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
||||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
|
||||||
import ru.soune.nocopy.service.register.AuthService;
|
import ru.soune.nocopy.service.register.AuthService;
|
||||||
import ru.soune.nocopy.service.file.FileEntityService;
|
import ru.soune.nocopy.service.file.FileEntityService;
|
||||||
import ru.soune.nocopy.service.file.FileStatsService;
|
import ru.soune.nocopy.service.file.FileStatsService;
|
||||||
@@ -36,8 +35,6 @@ public class FileEntityHandler implements RequestHandler {
|
|||||||
|
|
||||||
private final FileEntityRepository fileEntityRepository;
|
private final FileEntityRepository fileEntityRepository;
|
||||||
|
|
||||||
private final CloudStorageService cloudStorageService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseResponse handle(BaseRequest request) {
|
public BaseResponse handle(BaseRequest request) {
|
||||||
try {
|
try {
|
||||||
@@ -305,7 +302,7 @@ public class FileEntityHandler implements RequestHandler {
|
|||||||
int fullDelete = fileRequest.getFullDelete() == null ? 0 : fileRequest.getFullDelete();
|
int fullDelete = fileRequest.getFullDelete() == null ? 0 : fileRequest.getFullDelete();
|
||||||
|
|
||||||
if (fileEntity.getStatus().equals(FileStatus.DELETED) || fullDelete == 1) {
|
if (fileEntity.getStatus().equals(FileStatus.DELETED) || fullDelete == 1) {
|
||||||
cloudStorageService.deleteFromStorageDisk(fileEntity);
|
fileEntityService.deleteFromDisk(fileEntity);
|
||||||
response = DeleteFileResponse.builder()
|
response = DeleteFileResponse.builder()
|
||||||
.fileId(fileRequest.getFileId())
|
.fileId(fileRequest.getFileId())
|
||||||
.message("File deleted from disk")
|
.message("File deleted from disk")
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package ru.soune.nocopy.handler;
|
package ru.soune.nocopy.handler;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -9,19 +8,17 @@ import ru.soune.nocopy.dto.BaseRequest;
|
|||||||
import ru.soune.nocopy.dto.BaseResponse;
|
import ru.soune.nocopy.dto.BaseResponse;
|
||||||
import ru.soune.nocopy.dto.MessageCode;
|
import ru.soune.nocopy.dto.MessageCode;
|
||||||
import ru.soune.nocopy.dto.file.ImageSearchRequest;
|
import ru.soune.nocopy.dto.file.ImageSearchRequest;
|
||||||
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
|
||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
import ru.soune.nocopy.entity.file.FileEntity;
|
||||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||||
import ru.soune.nocopy.service.file.CheckCounterService;
|
import ru.soune.nocopy.service.file.CheckCounterService;
|
||||||
import ru.soune.nocopy.service.search.SearchImageService;
|
import ru.soune.nocopy.service.search.SearchApiToYandexResponseMapper;
|
||||||
|
import ru.soune.nocopy.service.search.YandexSearchService;
|
||||||
import ru.soune.nocopy.service.search.GoogleVisionSearchService;
|
import ru.soune.nocopy.service.search.GoogleVisionSearchService;
|
||||||
import ru.soune.nocopy.service.tariff.TariffConstants;
|
import ru.soune.nocopy.service.tariff.TariffConstants;
|
||||||
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.util.Map;
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -30,7 +27,7 @@ public class ImageFoundRequestHandler implements RequestHandler {
|
|||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private final SearchImageService searchImageService;
|
private final YandexSearchService yandexSearchService;
|
||||||
|
|
||||||
private final GoogleVisionSearchService googleVisionSearchService;
|
private final GoogleVisionSearchService googleVisionSearchService;
|
||||||
|
|
||||||
@@ -40,6 +37,8 @@ public class ImageFoundRequestHandler implements RequestHandler {
|
|||||||
|
|
||||||
private final TariffInfoService tariffInfoService;
|
private final TariffInfoService tariffInfoService;
|
||||||
|
|
||||||
|
private final SearchApiToYandexResponseMapper mapper = new SearchApiToYandexResponseMapper();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseResponse handle(BaseRequest request) throws Exception {
|
public BaseResponse handle(BaseRequest request) throws Exception {
|
||||||
ImageSearchRequest imageSearchRequest = objectMapper.convertValue(request.getMessageBody(),
|
ImageSearchRequest imageSearchRequest = objectMapper.convertValue(request.getMessageBody(),
|
||||||
@@ -59,54 +58,13 @@ public class ImageFoundRequestHandler implements RequestHandler {
|
|||||||
//TODO uncommited when add billing
|
//TODO uncommited when add billing
|
||||||
// GoogleVisionSearchResponse googleVisionSearchResponse = googleVisionSearchService.searchByFileEntity(fileId);
|
// GoogleVisionSearchResponse googleVisionSearchResponse = googleVisionSearchService.searchByFileEntity(fileId);
|
||||||
|
|
||||||
String searchResponseYandex;
|
String yandexReverseSearchResponse = yandexSearchService.searchReverseByPublicUrl(fileEntity);
|
||||||
String searchResponseGoogle;
|
|
||||||
try {
|
|
||||||
searchResponseYandex = searchImageService.searchReverseByPublicUrl(fileEntity, "yandex_reverse_image",
|
|
||||||
"visual_matches");
|
|
||||||
searchResponseGoogle = searchImageService.searchReverseByPublicUrl(fileEntity, "google_lens",
|
|
||||||
"exact_matches");
|
|
||||||
} catch (TimeoutException e) {
|
|
||||||
log.warn("Search timeout for file {}, returning empty results", fileId);
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
|
||||||
"Search completed (partial)", Map.of(
|
|
||||||
"results", List.of(),
|
|
||||||
"total", 0,
|
|
||||||
"searchStatus", "timeout"
|
|
||||||
));
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Search failed for file {}", fileId, e);
|
|
||||||
return new BaseResponse(request.getMsgId(), 20007,
|
|
||||||
"Search service temporarily unavailable",
|
|
||||||
Map.of("fileId", fileId));
|
|
||||||
}
|
|
||||||
|
|
||||||
tariffInfoService.writeOffTokens(fileEntity.getUserId(), TariffConstants.TOKEN_VALUE_FOR_SEARCH);
|
tariffInfoService.writeOffTokens(fileEntity.getUserId(), TariffConstants.TOKEN_VALUE_FOR_SEARCH);
|
||||||
|
|
||||||
checkCounterService.incrementCheckCount(fileEntity.getUserId(), fileEntity.getMimeType());
|
checkCounterService.incrementCheckCount(fileEntity.getUserId(), fileEntity.getMimeType());
|
||||||
|
|
||||||
List<YandexSearchResponse.ImageResult> allYandexImages = searchImageService.getAllImagesWithoutPagination(
|
|
||||||
searchResponseYandex, "visual_matches");
|
|
||||||
List<YandexSearchResponse.ImageResult> allGoogleImages = searchImageService.getAllImagesWithoutPagination(
|
|
||||||
searchResponseGoogle, "exact_matches");
|
|
||||||
|
|
||||||
List<YandexSearchResponse.ImageResult> allUniqueImages = searchImageService.removeDuplicateUrls(allYandexImages,
|
|
||||||
allGoogleImages);
|
|
||||||
|
|
||||||
int page = imageSearchRequest.getPage() != null ? imageSearchRequest.getPage() : 1;
|
|
||||||
int pageSize = 5;
|
|
||||||
|
|
||||||
List<YandexSearchResponse.ImageResult> pagedResults = searchImageService.paginateResults(allUniqueImages, page,
|
|
||||||
pageSize * 2);
|
|
||||||
|
|
||||||
YandexSearchResponse finalResponse = new YandexSearchResponse();
|
|
||||||
finalResponse.setImages(pagedResults);
|
|
||||||
finalResponse.setPage(page);
|
|
||||||
finalResponse.setPageSize(pageSize * 2);
|
|
||||||
finalResponse.setTotalResults(allUniqueImages.size());
|
|
||||||
finalResponse.setTotalPages((int) Math.ceil((double) allUniqueImages.size() / (pageSize * 2)));
|
|
||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
||||||
MessageCode.SUCCESS.getDescription(), finalResponse);
|
MessageCode.SUCCESS.getDescription(),mapper.mapToYandexResponse(yandexReverseSearchResponse));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
package ru.soune.nocopy.handler;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import ru.soune.ReferralInvitee;
|
|
||||||
import ru.soune.ReferralLevel;
|
|
||||||
import ru.soune.ReferralService;
|
|
||||||
import ru.soune.ReferralStat;
|
|
||||||
import ru.soune.nocopy.dto.BaseRequest;
|
|
||||||
import ru.soune.nocopy.dto.BaseResponse;
|
|
||||||
import ru.soune.nocopy.dto.MessageCode;
|
|
||||||
import ru.soune.nocopy.dto.referral.ReferralRequest;
|
|
||||||
import ru.soune.nocopy.entity.user.AuthToken;
|
|
||||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ReferralHandler implements RequestHandler {
|
|
||||||
|
|
||||||
private final ReferralService referralService;
|
|
||||||
|
|
||||||
private final AuthTokenRepository authTokenRepository;
|
|
||||||
|
|
||||||
private final ObjectMapper mapper = new ObjectMapper();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseResponse handle(BaseRequest request) throws Exception {
|
|
||||||
ReferralRequest referralRequest = mapper.convertValue(request.getMessageBody(), ReferralRequest.class);
|
|
||||||
String action = referralRequest.getAction();
|
|
||||||
String token = referralRequest.getToken();
|
|
||||||
AuthToken authToken = authTokenRepository.findByToken(token).orElseThrow();
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "refill":
|
|
||||||
return handleRefill(request, authToken.getUser().getId(), referralRequest.getAmount());
|
|
||||||
case "levels":
|
|
||||||
return handleReferralLevels(request);
|
|
||||||
case "level":
|
|
||||||
return handleUserLevel(request, authToken.getUser().getId());
|
|
||||||
case "userStats" :
|
|
||||||
return handleUserStats(request, authToken.getUser().getId());
|
|
||||||
case "invitees":
|
|
||||||
return handleInvitees(referralRequest, request, authToken.getUser().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BaseResponse handleRefill(BaseRequest request, Long userId, Integer amount) {
|
|
||||||
referralService.onUserAccountRefill(userId, amount);
|
|
||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
|
||||||
MessageCode.SUCCESS.getDescription(), Map.of("account", "refill"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private BaseResponse handleReferralLevels(BaseRequest request) {
|
|
||||||
List<ReferralLevel> referralLevels = referralService.getReferralLevels();
|
|
||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
|
||||||
MessageCode.SUCCESS.getDescription(), referralLevels);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private BaseResponse handleUserLevel(BaseRequest request, Long userId) {
|
|
||||||
ReferralLevel referralLevelForUser = referralService.getReferralLevelForUser(userId);
|
|
||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
|
||||||
MessageCode.SUCCESS.getDescription(), referralLevelForUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BaseResponse handleUserStats(BaseRequest request, Long userId) {
|
|
||||||
ReferralStat referralStatForUser = referralService.getReferralStatForUser(userId);
|
|
||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
|
||||||
MessageCode.SUCCESS.getDescription(), referralStatForUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BaseResponse handleInvitees(ReferralRequest referralRequest, BaseRequest request, Long userId) {
|
|
||||||
List<ReferralInvitee> inviteeForUser = referralService.getInviteeForUser(userId, referralRequest.getPageSize(),
|
|
||||||
referralRequest.getPageNumber());
|
|
||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
|
||||||
MessageCode.SUCCESS.getDescription(), inviteeForUser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,16 +6,13 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.validation.BeanPropertyBindingResult;
|
import org.springframework.validation.BeanPropertyBindingResult;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import ru.soune.ReferralService;
|
|
||||||
import ru.soune.nocopy.dto.*;
|
import ru.soune.nocopy.dto.*;
|
||||||
import ru.soune.nocopy.dto.register.RegAnswer;
|
import ru.soune.nocopy.dto.register.RegAnswer;
|
||||||
import ru.soune.nocopy.dto.register.RegRequest;
|
import ru.soune.nocopy.dto.register.RegRequest;
|
||||||
import ru.soune.nocopy.entity.referral.Referral;
|
|
||||||
import ru.soune.nocopy.entity.user.AuthToken;
|
import ru.soune.nocopy.entity.user.AuthToken;
|
||||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||||
import ru.soune.nocopy.exception.ValidationException;
|
import ru.soune.nocopy.exception.ValidationException;
|
||||||
import ru.soune.nocopy.handler.validator.RegRequestValidator;
|
import ru.soune.nocopy.handler.validator.RegRequestValidator;
|
||||||
import ru.soune.nocopy.repository.ReferralJpaRepository;
|
|
||||||
import ru.soune.nocopy.repository.UserRepository;
|
import ru.soune.nocopy.repository.UserRepository;
|
||||||
import ru.soune.nocopy.service.register.AuthService;
|
import ru.soune.nocopy.service.register.AuthService;
|
||||||
import ru.soune.nocopy.service.mail.EmailService;
|
import ru.soune.nocopy.service.mail.EmailService;
|
||||||
@@ -37,10 +34,6 @@ public class RegRequestHandler implements RequestHandler {
|
|||||||
|
|
||||||
private final EmailService emailService;
|
private final EmailService emailService;
|
||||||
|
|
||||||
private final ReferralService referralService;
|
|
||||||
|
|
||||||
private final ReferralJpaRepository referralJpaRepository;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseResponse handle(BaseRequest request) throws ValidationException {
|
public BaseResponse handle(BaseRequest request) throws ValidationException {
|
||||||
RegRequest regRequest = objectMapper.convertValue(request.getMessageBody(), RegRequest.class);
|
RegRequest regRequest = objectMapper.convertValue(request.getMessageBody(), RegRequest.class);
|
||||||
@@ -56,19 +49,6 @@ public class RegRequestHandler implements RequestHandler {
|
|||||||
MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getDescription(), regAnswer));
|
MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getDescription(), regAnswer));
|
||||||
}
|
}
|
||||||
|
|
||||||
String referralLink = regRequest.getReferralLink();
|
|
||||||
|
|
||||||
if (referralLink != null) {
|
|
||||||
Referral referral = referralJpaRepository.findByReferralLink(referralLink);
|
|
||||||
if (referral == null) {
|
|
||||||
throw new NotValidFieldException("Referral link is not found:" + referralLink +
|
|
||||||
referralLink, new BaseResponse(request.getMsgId(),
|
|
||||||
MessageCode.REFERRAL_LINK_IS_NOT_EXIST.getCode(),
|
|
||||||
MessageCode.REFERRAL_LINK_IS_NOT_EXIST.getDescription(), Map.of(
|
|
||||||
"referralLink", referralLink)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BindingResult bindingResult = new BeanPropertyBindingResult(regRequest, "regRequest");
|
BindingResult bindingResult = new BeanPropertyBindingResult(regRequest, "regRequest");
|
||||||
|
|
||||||
regRequestValidator.validate(regRequest, bindingResult);
|
regRequestValidator.validate(regRequest, bindingResult);
|
||||||
@@ -96,15 +76,12 @@ public class RegRequestHandler implements RequestHandler {
|
|||||||
|
|
||||||
RegAnswer regAnswer = new RegAnswer();
|
RegAnswer regAnswer = new RegAnswer();
|
||||||
|
|
||||||
|
|
||||||
regAnswer.setUserId(authToken.getUser().getId());
|
regAnswer.setUserId(authToken.getUser().getId());
|
||||||
regAnswer.setToken(token);
|
regAnswer.setToken(token);
|
||||||
regAnswer.setVerified(true);
|
regAnswer.setVerified(true);
|
||||||
regAnswer.setActive(true);
|
regAnswer.setActive(true);
|
||||||
|
|
||||||
if (regRequest.getAccountType().equals("b2c")) {
|
|
||||||
referralService.onRegister(authToken.getUser().getId(), regRequest.getReferralLink());
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
||||||
MessageCode.SUCCESS.getDescription(), regAnswer);
|
MessageCode.SUCCESS.getDescription(), regAnswer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ import ru.soune.nocopy.dto.MessageCode;
|
|||||||
import ru.soune.nocopy.dto.tarriff.TariffDTO;
|
import ru.soune.nocopy.dto.tarriff.TariffDTO;
|
||||||
import ru.soune.nocopy.dto.tarriff.TariffRequest;
|
import ru.soune.nocopy.dto.tarriff.TariffRequest;
|
||||||
import ru.soune.nocopy.dto.tarriff.TariffResponse;
|
import ru.soune.nocopy.dto.tarriff.TariffResponse;
|
||||||
import ru.soune.nocopy.entity.user.AuthToken;
|
|
||||||
import ru.soune.nocopy.entity.user.User;
|
|
||||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
|
||||||
import ru.soune.nocopy.service.tariff.TariffService;
|
import ru.soune.nocopy.service.tariff.TariffService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -27,8 +24,6 @@ public class TariffHandler implements RequestHandler {
|
|||||||
|
|
||||||
private final TariffService tariffService;
|
private final TariffService tariffService;
|
||||||
|
|
||||||
private final AuthTokenRepository authTokenRepository;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseResponse handle(BaseRequest request) throws Exception {
|
public BaseResponse handle(BaseRequest request) throws Exception {
|
||||||
try {
|
try {
|
||||||
@@ -118,9 +113,7 @@ public class TariffHandler implements RequestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BaseResponse handleGetTariffs(BaseRequest request, TariffRequest tariffRequest) {
|
private BaseResponse handleGetTariffs(BaseRequest request, TariffRequest tariffRequest) {
|
||||||
AuthToken authToken = authTokenRepository.findByToken(tariffRequest.getUserToken()).orElseThrow();
|
List<TariffDTO> allTariffs = tariffService.getAllTariffs();
|
||||||
User user = authToken.getUser();
|
|
||||||
List<TariffDTO> allTariffs = tariffService.getTariffByAccountType(user);
|
|
||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
||||||
MessageCode.SUCCESS.getDescription(),
|
MessageCode.SUCCESS.getDescription(),
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import java.util.Optional;
|
|||||||
public interface FileEntityRepository extends JpaRepository<FileEntity, String> {
|
public interface FileEntityRepository extends JpaRepository<FileEntity, String> {
|
||||||
List<FileEntity> findByUserId(Long userId);
|
List<FileEntity> findByUserId(Long userId);
|
||||||
|
|
||||||
FileEntity findByIdAndUserId(String id, Long userId);
|
|
||||||
|
|
||||||
Optional<FileEntity> findByUserIdAndChecksum(Long userId, String imageHash);
|
Optional<FileEntity> findByUserIdAndChecksum(Long userId, String imageHash);
|
||||||
|
|
||||||
List<FileEntity> findByUserIdAndStatus(Long userId, FileStatus status);
|
List<FileEntity> findByUserIdAndStatus(Long userId, FileStatus status);
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
package ru.soune.nocopy.repository;
|
|
||||||
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
|
||||||
import org.springframework.data.jpa.repository.Query;
|
|
||||||
import org.springframework.data.repository.query.Param;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
import ru.soune.nocopy.entity.referral.Referral;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
public interface ReferralJpaRepository extends JpaRepository<Referral, Long> {
|
|
||||||
Referral findByReferralLink(String link);
|
|
||||||
|
|
||||||
Referral findByUserId(Long userId);
|
|
||||||
|
|
||||||
List<Referral> findByInviterId(Long inviterId);
|
|
||||||
|
|
||||||
@Query("SELECT r FROM Referral r WHERE r.inviterId = :inviterId AND r.isActive = true")
|
|
||||||
List<Referral> findByInviterIdWhenUserActivated(Long inviterId, Pageable pageable);
|
|
||||||
|
|
||||||
@Query("SELECT r FROM Referral r WHERE r.inviterId = :inviterId")
|
|
||||||
List<Referral> findByInviterId(Long inviterId, Pageable pageable);
|
|
||||||
|
|
||||||
@Modifying
|
|
||||||
@Query("UPDATE Referral r SET r.totalIncome = r.totalIncome + :amount WHERE r.userId = :userId")
|
|
||||||
void increaseIncome(@Param("userId") Long userId, @Param("amount") int amount);
|
|
||||||
|
|
||||||
@Modifying
|
|
||||||
@Query("UPDATE Referral r SET r.levelId = :newLevelId WHERE r.userId = :userId")
|
|
||||||
void updateLevel(@Param("userId") Long userId, @Param("newLevelId") String newLevelId);
|
|
||||||
|
|
||||||
@Modifying
|
|
||||||
@Query("UPDATE Referral r SET r.isActive = true WHERE r.userId = :userId")
|
|
||||||
void activateUser(@Param("userId") Long userId);
|
|
||||||
|
|
||||||
@Query("SELECT COUNT(r) FROM Referral r WHERE r.inviterId = :userId AND r.isActive = true")
|
|
||||||
int countActiveInvitees(@Param("userId") Long userId);
|
|
||||||
|
|
||||||
@Query("SELECT COUNT(r) FROM Referral r WHERE r.inviterId = :userId")
|
|
||||||
int countTotalInvitees(@Param("userId") Long userId);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package ru.soune.nocopy.repository;
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
import ru.soune.nocopy.entity.referral.ReferralLevelEntity;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
public interface ReferralLevelRepository extends JpaRepository<ReferralLevelEntity, Long> {
|
|
||||||
List<ReferralLevelEntity> findAllByOrderByMinInviteesAsc();
|
|
||||||
ReferralLevelEntity findById(String id);
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,5 @@ import java.util.Optional;
|
|||||||
public interface TariffRepository extends JpaRepository<Tariff, Long> {
|
public interface TariffRepository extends JpaRepository<Tariff, Long> {
|
||||||
Optional<Tariff> findByType(String type);
|
Optional<Tariff> findByType(String type);
|
||||||
List<Tariff> findAllByOrderByPriceAsc();
|
List<Tariff> findAllByOrderByPriceAsc();
|
||||||
List<Tariff> findByTariffAccountType(String typeAccount);
|
|
||||||
Tariff findByName(String name);
|
Tariff findByName(String name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
package ru.soune.nocopy.service.dadata;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import okhttp3.*;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import ru.soune.nocopy.dto.dadata.DaDataResponse;
|
|
||||||
import ru.soune.nocopy.dto.dadata.DaDataSuggestion;
|
|
||||||
import ru.soune.nocopy.dto.dadata.DaDataWrapper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class DaDataService {
|
|
||||||
|
|
||||||
@Value("${dadata.api-key}")
|
|
||||||
private String apiKey ;
|
|
||||||
|
|
||||||
@Value("${dadata.url}")
|
|
||||||
private String url ;
|
|
||||||
|
|
||||||
private final OkHttpClient httpClient;
|
|
||||||
|
|
||||||
public DaDataService() {
|
|
||||||
this.httpClient = new OkHttpClient.Builder()
|
|
||||||
.connectTimeout(5, TimeUnit.SECONDS)
|
|
||||||
.writeTimeout(5, TimeUnit.SECONDS)
|
|
||||||
.readTimeout(15, TimeUnit.SECONDS)
|
|
||||||
.callTimeout(20, TimeUnit.SECONDS)
|
|
||||||
.retryOnConnectionFailure(true)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DaDataResponse callDaData(String inn) throws IOException {
|
|
||||||
if (apiKey == null || apiKey.isBlank()) {
|
|
||||||
throw new IllegalStateException("SearchAPI key not configured");
|
|
||||||
}
|
|
||||||
|
|
||||||
String jsonBody = String.format("{\"query\": \"%s\"}", inn);
|
|
||||||
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url("https://suggestions.dadata.ru/suggestions/api/4_1/rs/findById/party")
|
|
||||||
.post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonBody))
|
|
||||||
.header("Accept", "application/json")
|
|
||||||
.header("Authorization", "Token " + apiKey)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try (Response response = httpClient.newCall(request).execute()) {
|
|
||||||
ResponseBody body = response.body();
|
|
||||||
if (body == null) {
|
|
||||||
throw new IOException("Empty response body");
|
|
||||||
}
|
|
||||||
|
|
||||||
String jsonResponse = body.string();
|
|
||||||
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
|
||||||
DaDataWrapper wrapper = mapper.readValue(jsonResponse, DaDataWrapper.class);
|
|
||||||
|
|
||||||
DaDataResponse daDataResponse = new DaDataResponse();
|
|
||||||
|
|
||||||
if (wrapper.getSuggestions() != null && !wrapper.getSuggestions().isEmpty()) {
|
|
||||||
DaDataSuggestion suggestion = wrapper.getSuggestions().get(0);
|
|
||||||
|
|
||||||
daDataResponse.setCompanyName(suggestion.getValue());
|
|
||||||
|
|
||||||
if (suggestion.getData() != null) {
|
|
||||||
daDataResponse.setInn(suggestion.getData().getInn());
|
|
||||||
|
|
||||||
if (suggestion.getData().getAddress() != null) {
|
|
||||||
daDataResponse.setAddress(suggestion.getData().getAddress().getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return daDataResponse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import ru.soune.nocopy.entity.user.ProtectedFileCheck;
|
import ru.soune.nocopy.entity.user.ProtectedFileCheck;
|
||||||
import ru.soune.nocopy.repository.ProtectedFileCheckRepository;
|
import ru.soune.nocopy.repository.ProtectedFileCheckRepository;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
@@ -16,10 +15,7 @@ import java.time.Instant;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -39,7 +35,6 @@ public class FileCleanupService {
|
|||||||
log.info("Starting cleanup of expired temporary files");
|
log.info("Starting cleanup of expired temporary files");
|
||||||
|
|
||||||
cleanupOldPeriods();
|
cleanupOldPeriods();
|
||||||
cleanupTempFilesNightly();
|
|
||||||
|
|
||||||
Path tempDir = Paths.get(basePath, "temp");
|
Path tempDir = Paths.get(basePath, "temp");
|
||||||
if (!Files.exists(tempDir)) {
|
if (!Files.exists(tempDir)) {
|
||||||
@@ -103,50 +98,4 @@ public class FileCleanupService {
|
|||||||
protectedFileCheckRepository.saveAll(checks);
|
protectedFileCheckRepository.saveAll(checks);
|
||||||
log.info("Cleaned up {} old protection usage records", checks.size());
|
log.info("Cleaned up {} old protection usage records", checks.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanupTempFilesNightly() {
|
|
||||||
log.info("Starting nightly cleanup of temporary files");
|
|
||||||
|
|
||||||
String tempDir = System.getProperty("java.io.tmpdir");
|
|
||||||
Path tempPath = Paths.get(tempDir);
|
|
||||||
|
|
||||||
if (!Files.exists(tempPath)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Instant cutoffTime = Instant.now().minus(24, ChronoUnit.HOURS); // 24 часа
|
|
||||||
int deletedCount = 0;
|
|
||||||
|
|
||||||
try (Stream<Path> files = Files.list(tempPath)) {
|
|
||||||
List<Path> tempFiles = files
|
|
||||||
.filter(path -> {
|
|
||||||
String filename = path.getFileName().toString();
|
|
||||||
return filename.contains("_") &&
|
|
||||||
!filename.endsWith(".tmp") &&
|
|
||||||
Files.isRegularFile(path);
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
for (Path filePath : tempFiles) {
|
|
||||||
try {
|
|
||||||
File file = filePath.toFile();
|
|
||||||
Instant lastModified = Instant.ofEpochMilli(file.lastModified());
|
|
||||||
|
|
||||||
if (lastModified.isBefore(cutoffTime)) {
|
|
||||||
boolean deleted = Files.deleteIfExists(filePath);
|
|
||||||
if (deleted) {
|
|
||||||
deletedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Error deleting file: {}", filePath, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Nightly cleanup deleted {} temporary files", deletedCount);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Failed to list temp directory", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,13 @@ import ru.soune.nocopy.repository.SimilarImageProjection;
|
|||||||
import ru.soune.nocopy.repository.UserRepository;
|
import ru.soune.nocopy.repository.UserRepository;
|
||||||
import ru.soune.nocopy.service.FileSimilarityService;
|
import ru.soune.nocopy.service.FileSimilarityService;
|
||||||
import ru.soune.nocopy.service.ImageHashService;
|
import ru.soune.nocopy.service.ImageHashService;
|
||||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -43,8 +42,6 @@ public class FileEntityService {
|
|||||||
|
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
private final CloudStorageService cloudStorageService;
|
|
||||||
|
|
||||||
@Transactional(noRollbackFor = DuplicateImageException.class)
|
@Transactional(noRollbackFor = DuplicateImageException.class)
|
||||||
public FileEntity createFromUploadSession(FileUploadSession session, String checksum) {
|
public FileEntity createFromUploadSession(FileUploadSession session, String checksum) {
|
||||||
log.info("Creating FileEntity for upload session: {}", session.getUploadId());
|
log.info("Creating FileEntity for upload session: {}", session.getUploadId());
|
||||||
@@ -58,7 +55,7 @@ public class FileEntityService {
|
|||||||
|
|
||||||
Map<String, Long> imageHash = Map.of();
|
Map<String, Long> imageHash = Map.of();
|
||||||
|
|
||||||
if (!session.getFileType().startsWith("video")) {
|
if (session.getFileType().startsWith("image")) {
|
||||||
imageHash = imageHashService.calculateHash(filePath);
|
imageHash = imageHashService.calculateHash(filePath);
|
||||||
List<SimilarImageProjection> duplicatedByHash = fileSimilarityService.findDuplicatedByHash(
|
List<SimilarImageProjection> duplicatedByHash = fileSimilarityService.findDuplicatedByHash(
|
||||||
imageHash.get("hi"), imageHash.get("low"));
|
imageHash.get("hi"), imageHash.get("low"));
|
||||||
@@ -119,22 +116,43 @@ public class FileEntityService {
|
|||||||
return convertToResponse(fileEntity, version);
|
return convertToResponse(fileEntity, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public FileEntityResponse getByFilePath(String filePath, int version) {
|
||||||
|
FileEntity fileEntity = fileEntityRepository.findByFilePath(filePath)
|
||||||
|
.orElseThrow(() -> new FileEntityNotFoundException("Path: " + filePath));
|
||||||
|
|
||||||
|
return convertToResponse(fileEntity, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public FileResponse getAllUserFiles(Long userId, int version) {
|
||||||
|
List<FileEntity> fileEntities = fileEntityRepository.findByUserIdAndStatus(
|
||||||
|
userId, FileStatus.ACTIVE);
|
||||||
|
|
||||||
|
List<FileEntityResponse> files = fileEntities.stream()
|
||||||
|
.map(file -> convertToResponse(file, version))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
long totalSize = fileEntities.stream()
|
||||||
|
.mapToLong(FileEntity::getFileSize)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
return FileResponse.builder()
|
||||||
|
.files(files)
|
||||||
|
.totalCount(files.size())
|
||||||
|
.totalSize(totalSize)
|
||||||
|
.formattedTotalSize(formatFileSize(totalSize))
|
||||||
|
.page(1)
|
||||||
|
.pageSize(files.size())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public FileResponse getUserFiles(Long userId, int page, int pageSize, int version) {
|
public FileResponse getUserFiles(Long userId, int page, int pageSize, int version) {
|
||||||
User user = userRepository.findById(userId).orElseThrow();
|
List<FileEntity> allFiles = fileEntityRepository.findByUserIdAndStatus(
|
||||||
List<FileEntity> allFiles = new ArrayList<>();
|
userId, FileStatus.ACTIVE);
|
||||||
|
|
||||||
if (user.getCompany() != null) {
|
|
||||||
for (Long uId : userRepository.findByCompanyId(user.getCompany().getId()).stream()
|
|
||||||
.map(User::getId)
|
|
||||||
.toList()) {
|
|
||||||
allFiles.addAll(fileEntityRepository.findByUserIdAndStatus(uId, FileStatus.ACTIVE));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
allFiles = fileEntityRepository.findByUserIdAndStatus(
|
|
||||||
userId, FileStatus.ACTIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = (page - 1) * pageSize;
|
int start = (page - 1) * pageSize;
|
||||||
int end = Math.min(start + pageSize, allFiles.size());
|
int end = Math.min(start + pageSize, allFiles.size());
|
||||||
@@ -170,6 +188,19 @@ public class FileEntityService {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public FileEntity markAsDeleted(FileEntity fileEntity) throws IOException {
|
||||||
|
fileEntity.setStatus(FileStatus.DELETED);
|
||||||
|
fileEntity.setUpdatedAt(LocalDateTime.now());
|
||||||
|
fileEntity.setProtectionStatus(ProtectionStatus.NOT_PROTECTED);
|
||||||
|
|
||||||
|
Path path = Paths.get(fileEntity.getProtectedFilePath());
|
||||||
|
Files.deleteIfExists(path);
|
||||||
|
fileEntity.setProtectedFilePath("");
|
||||||
|
|
||||||
|
return fileEntityRepository.save(fileEntity);
|
||||||
|
}
|
||||||
|
|
||||||
public void softDeleteFileWithHash(FileEntity fileEntity) throws IOException {
|
public void softDeleteFileWithHash(FileEntity fileEntity) throws IOException {
|
||||||
if (fileEntity.getImageHash() != null) {
|
if (fileEntity.getImageHash() != null) {
|
||||||
fileEntity.setImageHash(null);
|
fileEntity.setImageHash(null);
|
||||||
@@ -186,8 +217,16 @@ public class FileEntityService {
|
|||||||
fileEntityRepository.save(fileEntity);
|
fileEntityRepository.save(fileEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteFromDisk(String deleteFilePath) throws IOException {
|
public void deleteFromDisk(FileEntity fileEntity) throws IOException {
|
||||||
Files.deleteIfExists(Paths.get(deleteFilePath));
|
Path path = Paths.get(fileEntity.getFilePath());
|
||||||
|
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.delete(path);
|
||||||
|
|
||||||
|
fileEntityRepository.delete(fileEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
@@ -205,7 +244,7 @@ public class FileEntityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public FileEntity writeProtectedFile(String id, byte[] data, String fileExt) throws IOException {
|
public void writeProtectedFile(String id, byte[] data, String fileExt) throws IOException {
|
||||||
FileEntity fileEntity = fileEntityRepository.findById(id)
|
FileEntity fileEntity = fileEntityRepository.findById(id)
|
||||||
.orElseThrow(() -> new RuntimeException("File not found: " + id));
|
.orElseThrow(() -> new RuntimeException("File not found: " + id));
|
||||||
|
|
||||||
@@ -224,14 +263,14 @@ public class FileEntityService {
|
|||||||
fileEntity.setFileExtension(extension);
|
fileEntity.setFileExtension(extension);
|
||||||
fileEntity.setProtectionStatus(ProtectionStatus.PROTECTED);
|
fileEntity.setProtectionStatus(ProtectionStatus.PROTECTED);
|
||||||
|
|
||||||
return fileEntityRepository.save(fileEntity);
|
fileEntityRepository.save(fileEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearTempFiles(long userId) {
|
public void clearTempFiles(long userId) throws IOException {
|
||||||
List<FileEntity> fileByUserIdAndStatus = fileEntityRepository.findFileByUserIdAndStatus(userId, FileStatus.TEMP);
|
List<FileEntity> fileByUserIdAndStatus = fileEntityRepository.findFileByUserIdAndStatus(userId, FileStatus.TEMP);
|
||||||
|
|
||||||
for (FileEntity fileEntity : fileByUserIdAndStatus) {
|
for (FileEntity fileEntity : fileByUserIdAndStatus) {
|
||||||
cloudStorageService.deleteFromStorageDisk(fileEntity);
|
deleteFromDisk(fileEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +282,24 @@ public class FileEntityService {
|
|||||||
return fileEntityRepository.findFileIdByFilePath(filePath);
|
return fileEntityRepository.findFileIdByFilePath(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File getFileById(String id) {
|
||||||
|
try {
|
||||||
|
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||||
|
new RuntimeException("File not found: " + id));
|
||||||
|
|
||||||
|
File file = new File(fileEntity.getFilePath());
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new RuntimeException("File not found on disk: " + fileEntity.getFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error getting file: {}", id, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Path prepareProtectedPath(FileEntity fileEntity, String extension) throws IOException {
|
public Path prepareProtectedPath(FileEntity fileEntity, String extension) throws IOException {
|
||||||
Path originalPath = Paths.get(fileEntity.getFilePath());
|
Path originalPath = Paths.get(fileEntity.getFilePath());
|
||||||
|
|
||||||
@@ -279,18 +336,6 @@ public class FileEntityService {
|
|||||||
fileEntityRepository.save(fileEntity);
|
fileEntityRepository.save(fileEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String formatFileSize(long size) {
|
|
||||||
if (size < 1024) {
|
|
||||||
return size + " B";
|
|
||||||
} else if (size < 1024 * 1024) {
|
|
||||||
return String.format("%.1f KB", size / 1024.0);
|
|
||||||
} else if (size < 1024 * 1024 * 1024) {
|
|
||||||
return String.format("%.1f MB", size / (1024.0 * 1024.0));
|
|
||||||
} else {
|
|
||||||
return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkFileExistsOnDisk(String filePath) {
|
private boolean checkFileExistsOnDisk(String filePath) {
|
||||||
try {
|
try {
|
||||||
return Files.exists(Paths.get(filePath));
|
return Files.exists(Paths.get(filePath));
|
||||||
@@ -301,7 +346,7 @@ public class FileEntityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String determineFileExtension(String fileExt, FileEntity fileEntity) {
|
private String determineFileExtension(String fileExt, FileEntity fileEntity) {
|
||||||
if (fileExt != null) {
|
if (fileExt != null && !fileExt.trim().isEmpty()) {
|
||||||
return fileExt;
|
return fileExt;
|
||||||
} else {
|
} else {
|
||||||
return fileEntity.getFileExtension();
|
return fileEntity.getFileExtension();
|
||||||
@@ -331,9 +376,7 @@ public class FileEntityService {
|
|||||||
.existsOnDisk(existsOnDisk)
|
.existsOnDisk(existsOnDisk)
|
||||||
.supportId(fileEntity.getSupportId())
|
.supportId(fileEntity.getSupportId())
|
||||||
.protectStatus(fileEntity.getProtectionStatus().toString())
|
.protectStatus(fileEntity.getProtectionStatus().toString())
|
||||||
.fileName(fileEntity.getOriginalFileName().replace("." +
|
.fileName(fileEntity.getOriginalFileName())
|
||||||
fileEntity.getFileExtension(), "") + "_nocopy_protected" +
|
|
||||||
"." + fileEntity.getFileExtension())
|
|
||||||
.ownerName(user.getFullName())
|
.ownerName(user.getFullName())
|
||||||
.ownerEmail(user.getEmail())
|
.ownerEmail(user.getEmail())
|
||||||
.ownerCompany(user.getCompanyName())
|
.ownerCompany(user.getCompanyName())
|
||||||
@@ -343,4 +386,16 @@ public class FileEntityService {
|
|||||||
.protectedFilePath(fileEntity.getProtectedFilePath())
|
.protectedFilePath(fileEntity.getProtectedFilePath())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String formatFileSize(long size) {
|
||||||
|
if (size < 1024) {
|
||||||
|
return size + " B";
|
||||||
|
} else if (size < 1024 * 1024) {
|
||||||
|
return String.format("%.1f KB", size / 1024.0);
|
||||||
|
} else if (size < 1024 * 1024 * 1024) {
|
||||||
|
return String.format("%.1f MB", size / (1024.0 * 1024.0));
|
||||||
|
} else {
|
||||||
|
return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -55,12 +55,6 @@ public class FileStatsService {
|
|||||||
.audiosCheck(calculateMediaByStatus(files, FileType.AUDIO, FileStatus.CHECKED))
|
.audiosCheck(calculateMediaByStatus(files, FileType.AUDIO, FileStatus.CHECKED))
|
||||||
.audiosViolations(calculateMediaByStatus(files, FileType.AUDIO, FileStatus.VIOLATION))
|
.audiosViolations(calculateMediaByStatus(files, FileType.AUDIO, FileStatus.VIOLATION))
|
||||||
.protectedAudioFilesCount(protectedUserFiles(files, FileType.AUDIO))
|
.protectedAudioFilesCount(protectedUserFiles(files, FileType.AUDIO))
|
||||||
|
|
||||||
.documentSize(calculateMediaSize(files, FileType.DOCUMENT))
|
|
||||||
.documentCount(calculateMediaCount(files, FileType.DOCUMENT))
|
|
||||||
.documentCheck(calculateMediaByStatus(files, FileType.DOCUMENT, FileStatus.CHECKED))
|
|
||||||
.documentViolations(calculateMediaByStatus(files, FileType.DOCUMENT, FileStatus.VIOLATION))
|
|
||||||
.protectedDocumentFilesCount(protectedUserFiles(files, FileType.DOCUMENT))
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package ru.soune.nocopy.service.file;
|
|||||||
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import ru.soune.nocopy.dto.file.UploadProgressResponse;
|
import ru.soune.nocopy.dto.file.UploadProgressResponse;
|
||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
|
||||||
import ru.soune.nocopy.entity.file.FileStatus;
|
import ru.soune.nocopy.entity.file.FileStatus;
|
||||||
import ru.soune.nocopy.entity.file.FileUploadSession;
|
import ru.soune.nocopy.entity.file.FileUploadSession;
|
||||||
|
|
||||||
@@ -20,5 +19,5 @@ public interface FileUploadService {
|
|||||||
|
|
||||||
UploadProgressResponse getUploadProgress(String uploadId);
|
UploadProgressResponse getUploadProgress(String uploadId);
|
||||||
|
|
||||||
FileEntity completeFileProcessingAsync(FileUploadSession session, FileStatus status);
|
void completeFileProcessingAsync(FileUploadSession session, FileStatus status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
package ru.soune.nocopy.service.file.cloud;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
|
||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
|
||||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
|
||||||
import software.amazon.awssdk.auth.credentials.AwsCredentials;
|
|
||||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
|
||||||
import software.amazon.awssdk.core.sync.RequestBody;
|
|
||||||
import software.amazon.awssdk.http.apache.ApacheHttpClient;
|
|
||||||
import software.amazon.awssdk.regions.Region;
|
|
||||||
import software.amazon.awssdk.services.s3.S3Client;
|
|
||||||
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
|
|
||||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class CloudStorageService {
|
|
||||||
|
|
||||||
private final FileEntityRepository fileEntityRepository;
|
|
||||||
|
|
||||||
@Value("${yandex.cloud.key}")
|
|
||||||
private String key;
|
|
||||||
|
|
||||||
@Value("${yandex.cloud.secret-key}")
|
|
||||||
private String secretKey;
|
|
||||||
|
|
||||||
@Value("${yandex.cloud.region}")
|
|
||||||
private String region;
|
|
||||||
|
|
||||||
@Value("${yandex.cloud.s3-endpoint}")
|
|
||||||
private String s3endpoint;
|
|
||||||
|
|
||||||
@Value("${yandex.cloud.bucket}")
|
|
||||||
private String bucket;
|
|
||||||
|
|
||||||
public void saveFilesToStorage(String mimeType, String extension, String sendToCloudFilePath) {
|
|
||||||
AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
|
|
||||||
|
|
||||||
S3Client s3Client = S3Client.builder()
|
|
||||||
.httpClient(ApacheHttpClient.create())
|
|
||||||
.region(Region.of(region))
|
|
||||||
.endpointOverride(URI.create(s3endpoint))
|
|
||||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
String mimeTypeFromFile = mimeType.equals("document") ? "application" : mimeType;
|
|
||||||
String contentType = mimeTypeFromFile + "/" + extension;
|
|
||||||
String filePath = "files" + sendToCloudFilePath;
|
|
||||||
|
|
||||||
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
|
|
||||||
.bucket(bucket)
|
|
||||||
.key(filePath)
|
|
||||||
.contentType(contentType)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try {
|
|
||||||
s3Client.putObject(putObjectRequest,
|
|
||||||
RequestBody.fromBytes(Files.readAllBytes(Paths.get(sendToCloudFilePath))));
|
|
||||||
Files.deleteIfExists(Paths.get(sendToCloudFilePath));
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Failed to read file: {}", filePath, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public File readFileFromStorageByPath(String filePath) throws IOException {
|
|
||||||
GetObjectRequest objectRequest = GetObjectRequest.builder()
|
|
||||||
.bucket(bucket)
|
|
||||||
.key("files" + filePath)
|
|
||||||
.build();
|
|
||||||
AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
|
|
||||||
|
|
||||||
S3Client s3Client = S3Client.builder()
|
|
||||||
.httpClient(ApacheHttpClient.create())
|
|
||||||
.region(Region.of(region))
|
|
||||||
.endpointOverride(URI.create(s3endpoint))
|
|
||||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
String tempDir = System.getProperty("java.io.tmpdir");
|
|
||||||
String fileName = filePath.replace("/", "_");
|
|
||||||
File tempFile = new File(tempDir, fileName);
|
|
||||||
|
|
||||||
s3Client.getObject(objectRequest, tempFile.toPath());
|
|
||||||
|
|
||||||
return tempFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteFileFromStorage(String filePath) {
|
|
||||||
if (filePath == null || filePath.isEmpty()) {
|
|
||||||
log.warn("Attempted to delete file with empty path");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
|
|
||||||
|
|
||||||
S3Client s3Client = S3Client.builder()
|
|
||||||
.httpClient(ApacheHttpClient.create())
|
|
||||||
.region(Region.of(region))
|
|
||||||
.endpointOverride(URI.create(s3endpoint))
|
|
||||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
String cloudKey = "files" + filePath;
|
|
||||||
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
s3Client.headObject(builder -> builder.bucket(bucket).key(cloudKey).build());
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("File does not exist in cloud storage: {}", cloudKey);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
s3Client.deleteObject(builder -> builder.bucket(bucket).key(cloudKey).build());
|
|
||||||
|
|
||||||
log.info("File successfully deleted from cloud storage: {}", cloudKey);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to delete file from cloud storage: {}", cloudKey, e);
|
|
||||||
throw new RuntimeException("Failed to delete file from cloud storage: " + cloudKey, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteFromStorageDisk(FileEntity fileEntity) {
|
|
||||||
deleteFileFromStorage(fileEntity.getFilePath());
|
|
||||||
|
|
||||||
if (fileEntity.getProtectedFilePath() != null) {
|
|
||||||
deleteFileFromStorage(fileEntity.getProtectedFilePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
fileEntityRepository.delete(fileEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package ru.soune.nocopy.service.file.impl;
|
|
||||||
|
|
||||||
import com.vrt.fileprotection.FileProtector;
|
|
||||||
import com.vrt.fileprotection.documents.DocumentLocalSearch;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
|
||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
|
||||||
import ru.soune.nocopy.util.FileUtil;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class DocumentLocalSearchImpl implements DocumentLocalSearch {
|
|
||||||
|
|
||||||
private final FileUtil fileUtil;
|
|
||||||
|
|
||||||
private final FileEntityRepository fileEntityRepository;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable FileProtector.FileInfo findByIds(@NotNull String ownerId, @NotNull String fileId) {
|
|
||||||
FileEntity fileEntity = fileEntityRepository.findByIdAndUserId(fileId, Long.valueOf(ownerId));
|
|
||||||
FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity);
|
|
||||||
log.info("fileInfo: {}", fileInfo);
|
|
||||||
|
|
||||||
return fileInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ package ru.soune.nocopy.service.file.impl;
|
|||||||
import com.vrt.NoCopyFileService;
|
import com.vrt.NoCopyFileService;
|
||||||
import com.vrt.fileprotection.FileProtector;
|
import com.vrt.fileprotection.FileProtector;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.persistence.EntityManager;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -25,7 +24,6 @@ import ru.soune.nocopy.service.FileSimilarityService;
|
|||||||
import ru.soune.nocopy.service.ImageHashService;
|
import ru.soune.nocopy.service.ImageHashService;
|
||||||
import ru.soune.nocopy.service.file.FileEntityService;
|
import ru.soune.nocopy.service.file.FileEntityService;
|
||||||
import ru.soune.nocopy.service.file.FileUploadService;
|
import ru.soune.nocopy.service.file.FileUploadService;
|
||||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
|
||||||
import ru.soune.nocopy.service.tariff.TariffConstants;
|
import ru.soune.nocopy.service.tariff.TariffConstants;
|
||||||
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
||||||
import ru.soune.nocopy.util.FileUtil;
|
import ru.soune.nocopy.util.FileUtil;
|
||||||
@@ -85,8 +83,6 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
|
|
||||||
private final TariffInfoService tariffInfoService;
|
private final TariffInfoService tariffInfoService;
|
||||||
|
|
||||||
private final CloudStorageService cloudStorageService;
|
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
try {
|
try {
|
||||||
@@ -181,6 +177,7 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public UploadProgressResponse uploadChunk(String uploadId, Integer chunkNumber,
|
public UploadProgressResponse uploadChunk(String uploadId, Integer chunkNumber,
|
||||||
MultipartFile chunkFile, Integer findSimilar) {
|
MultipartFile chunkFile, Integer findSimilar) {
|
||||||
FileUploadSession session = sessionRepository.findById(uploadId)
|
FileUploadSession session = sessionRepository.findById(uploadId)
|
||||||
@@ -222,8 +219,7 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
@Async("fileUploadTaskExecutor")
|
@Async("fileUploadTaskExecutor")
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public FileEntity completeFileProcessingAsync(FileUploadSession session, FileStatus status) {
|
public void completeFileProcessingAsync(FileUploadSession session, FileStatus status) {
|
||||||
FileEntity saved = null;
|
|
||||||
try {
|
try {
|
||||||
Path filePath = Paths.get(session.getFilePath());
|
Path filePath = Paths.get(session.getFilePath());
|
||||||
String checksum = calculateChecksum(filePath);
|
String checksum = calculateChecksum(filePath);
|
||||||
@@ -242,7 +238,7 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
.status(status)
|
.status(status)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
saved = fileEntityRepository.save(fileEntity);
|
FileEntity saved = fileEntityRepository.save(fileEntity);
|
||||||
|
|
||||||
if (session.getFileType().equals("image")) {
|
if (session.getFileType().equals("image")) {
|
||||||
Map<String, Long> hash = imageHashService.calculateHash(filePath);
|
Map<String, Long> hash = imageHashService.calculateHash(filePath);
|
||||||
@@ -252,13 +248,16 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
|
|
||||||
cleanupSessionFiles(session);
|
cleanupSessionFiles(session);
|
||||||
|
|
||||||
|
if (status != FileStatus.TEMP) {
|
||||||
|
noCopyFileService.addFile(fileUtil.createFileInfo(fileEntity));
|
||||||
|
}
|
||||||
|
|
||||||
log.info("File processing completed for session: {}", session.getUploadId());
|
log.info("File processing completed for session: {}", session.getUploadId());
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to complete file processing for session {}: {}",
|
log.error("Failed to complete file processing for session {}: {}",
|
||||||
session.getUploadId(), e.getMessage(), e);
|
session.getUploadId(), e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return saved;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateSession(FileUploadSession session) {
|
private void validateSession(FileUploadSession session) {
|
||||||
@@ -290,9 +289,6 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private EntityManager entityManager;
|
|
||||||
|
|
||||||
private UploadProgressResponse processChunk(FileUploadSession session, Integer chunkNumber, MultipartFile chunkFile,
|
private UploadProgressResponse processChunk(FileUploadSession session, Integer chunkNumber, MultipartFile chunkFile,
|
||||||
Integer findSimilar) {
|
Integer findSimilar) {
|
||||||
String chunkPath = null;
|
String chunkPath = null;
|
||||||
@@ -333,20 +329,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
fileEntityService.clearTempFiles(session.getUserId());
|
fileEntityService.clearTempFiles(session.getUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
FileEntity fileEntity = completeFileProcessingAsync(session, status);
|
completeFileProcessingAsync(session, status);
|
||||||
|
|
||||||
fileEntityRepository.flush();
|
|
||||||
|
|
||||||
entityManager.clear();
|
|
||||||
|
|
||||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
|
||||||
fileEntity.getFilePath());
|
|
||||||
|
|
||||||
if (status != FileStatus.TEMP) {
|
|
||||||
FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity);
|
|
||||||
|
|
||||||
noCopyFileService.addFile(fileInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status != FileStatus.TEMP) {
|
if (status != FileStatus.TEMP) {
|
||||||
String fileType = session.getFileType();
|
String fileType = session.getFileType();
|
||||||
@@ -393,13 +377,13 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
|
|
||||||
switch (fileType) {
|
switch (fileType) {
|
||||||
case "video":
|
case "video":
|
||||||
return TariffConstants.VIDEO_TOKEN_VALUE;
|
cost = TariffConstants.VIDEO_TOKEN_VALUE;
|
||||||
case "image":
|
case "image":
|
||||||
return TariffConstants.IMAGE_TOKEN_VALUE;
|
cost = TariffConstants.IMAGE_TOKEN_VALUE;
|
||||||
case "audio":
|
case "audio":
|
||||||
return TariffConstants.AUDIO_TOKEN_VALUE;
|
cost = TariffConstants.AUDIO_TOKEN_VALUE;
|
||||||
case "document":
|
case "pdf":
|
||||||
return TariffConstants.PDF_TOKEN_VALUE;
|
cost = TariffConstants.PDF_TOKEN_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cost;
|
return cost;
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
|
||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
|
||||||
import ru.soune.nocopy.service.file.FileEntityService;
|
import ru.soune.nocopy.service.file.FileEntityService;
|
||||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -27,55 +24,22 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
|||||||
|
|
||||||
private final FileEntityService fileEntityService;
|
private final FileEntityService fileEntityService;
|
||||||
|
|
||||||
private final CloudStorageService cloudStorageService;
|
|
||||||
|
|
||||||
private final FileEntityRepository fileEntityRepository;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public File getImageFile(@NotNull String id) {
|
public File getImageFile(@NotNull String id) {
|
||||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
return fileEntityService.getFileById(id);
|
||||||
new RuntimeException("File not found: " + id));
|
|
||||||
try {
|
|
||||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public File getVideoFile(@NotNull String id) {
|
public File getVideoFile(@NotNull String id) {
|
||||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
return fileEntityService.getFileById(id);
|
||||||
new RuntimeException("File not found: " + id));
|
|
||||||
try {
|
|
||||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public File getAudioFile(@NotNull String id) {
|
public File getAudioFile(@NotNull String id) {
|
||||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
return fileEntityService.getFileById(id);
|
||||||
new RuntimeException("File not found: " + id));
|
|
||||||
try {
|
|
||||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable File getDocument(@NotNull String id) {
|
|
||||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
|
||||||
new RuntimeException("File not found: " + id));
|
|
||||||
try {
|
|
||||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -110,11 +74,7 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
|||||||
@Override
|
@Override
|
||||||
public com.vrt.fileprotection.OperationResult writeAudioFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
public com.vrt.fileprotection.OperationResult writeAudioFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||||
try {
|
try {
|
||||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, fileExt);
|
fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||||
|
|
||||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
|
||||||
fileEntity.getProtectedFilePath());
|
|
||||||
|
|
||||||
return OperationResult.Companion.success();
|
return OperationResult.Companion.success();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to create protected file: {}", id, e);
|
log.error("Failed to create protected file: {}", id, e);
|
||||||
@@ -126,11 +86,7 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
|||||||
@Override
|
@Override
|
||||||
public com.vrt.fileprotection.OperationResult writeVideoFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
public com.vrt.fileprotection.OperationResult writeVideoFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||||
try {
|
try {
|
||||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, fileExt);
|
fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||||
|
|
||||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
|
||||||
fileEntity.getProtectedFilePath());
|
|
||||||
|
|
||||||
return OperationResult.Companion.success();
|
return OperationResult.Companion.success();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to create protected file: {}", id, e);
|
log.error("Failed to create protected file: {}", id, e);
|
||||||
@@ -142,25 +98,7 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
|||||||
@Override
|
@Override
|
||||||
public com.vrt.fileprotection.OperationResult writeImageFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
public com.vrt.fileprotection.OperationResult writeImageFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||||
try {
|
try {
|
||||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, fileExt);
|
fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||||
|
|
||||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
|
||||||
fileEntity.getProtectedFilePath());
|
|
||||||
|
|
||||||
return OperationResult.Companion.success();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to create protected file: {}", id, e);
|
|
||||||
return OperationResult.Companion.failure("Failed to create protected file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull OperationResult writeDocumentFile(@NotNull String id, @NotNull byte[] data) {
|
|
||||||
try {
|
|
||||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, null);
|
|
||||||
|
|
||||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
|
||||||
fileEntity.getProtectedFilePath());
|
|
||||||
return OperationResult.Companion.success();
|
return OperationResult.Companion.success();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to create protected file: {}", id, e);
|
log.error("Failed to create protected file: {}", id, e);
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
package ru.soune.nocopy.service.referral;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import ru.soune.ReferralLevel;
|
|
||||||
import ru.soune.ReferralLevelProvider;
|
|
||||||
import ru.soune.nocopy.entity.referral.ReferralLevelEntity;
|
|
||||||
import ru.soune.nocopy.repository.ReferralLevelRepository;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ReferralLevelProviderImpl implements ReferralLevelProvider {
|
|
||||||
|
|
||||||
private final ReferralLevelRepository referralLevelRepository;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ReferralLevel> getAvailableReferralLevels() {
|
|
||||||
List<ReferralLevelEntity> entities = referralLevelRepository.findAll();
|
|
||||||
|
|
||||||
return entities.stream()
|
|
||||||
.map(this::createReferralLevel)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getInitialReferralLevelId() {
|
|
||||||
return "bronze";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReferralLevel getReferralLevelById(String levelId) {
|
|
||||||
return createReferralLevel(referralLevelRepository.findById(levelId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReferralLevel createReferralLevel(ReferralLevelEntity entity) {
|
|
||||||
return new ReferralLevel() {
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return entity.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return entity.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getShare() {
|
|
||||||
return entity.getRewardPercentage();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinInvitee() {
|
|
||||||
return entity.getMinInvitees();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxInvitee() {
|
|
||||||
switch (entity.getId()) {
|
|
||||||
case "bronze": return 5;
|
|
||||||
case "silver": return 15;
|
|
||||||
case "gold": return 50;
|
|
||||||
case "platinum": return Integer.MAX_VALUE;
|
|
||||||
default: return Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getNext() {
|
|
||||||
switch (entity.getId()) {
|
|
||||||
case "bronze": return "silver";
|
|
||||||
case "silver": return "gold";
|
|
||||||
case "gold": return "platinum";
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
package ru.soune.nocopy.service.referral;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.springframework.data.domain.PageRequest;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import ru.soune.Referral;
|
|
||||||
import ru.soune.ReferralInvitee;
|
|
||||||
import ru.soune.ReferralRepo;
|
|
||||||
import ru.soune.nocopy.entity.user.User;
|
|
||||||
import ru.soune.nocopy.repository.ReferralJpaRepository;
|
|
||||||
import ru.soune.nocopy.repository.UserRepository;
|
|
||||||
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Slf4j
|
|
||||||
@Transactional
|
|
||||||
public class ReferralRepoImpl implements ReferralRepo {
|
|
||||||
|
|
||||||
private final ReferralJpaRepository referralRepository;
|
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull String getUserReferralLink(long userId) {
|
|
||||||
ru.soune.nocopy.entity.referral.Referral referral =
|
|
||||||
referralRepository.findByUserId(userId);
|
|
||||||
|
|
||||||
if (referral != null && referral.getReferralLink() != null &&
|
|
||||||
!referral.getReferralLink().isEmpty()) {
|
|
||||||
return referral.getReferralLink();
|
|
||||||
}
|
|
||||||
|
|
||||||
String newLink = "ref-" + userId + "-" + UUID.randomUUID().toString().substring(0, 8);
|
|
||||||
|
|
||||||
if (referral == null) {
|
|
||||||
referral = new ru.soune.nocopy.entity.referral.Referral();
|
|
||||||
referral.setUserId(userId);
|
|
||||||
referral.setLevelId("bronze");
|
|
||||||
referral.setActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
referral.setReferralLink(newLink);
|
|
||||||
|
|
||||||
referralRepository.save(referral);
|
|
||||||
|
|
||||||
return newLink;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Long getUserIdByLink(@NotNull String link) {
|
|
||||||
ru.soune.nocopy.entity.referral.Referral referral = referralRepository.findByReferralLink(link);
|
|
||||||
return referral != null ? referral.getUserId() : 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Long getInviterIdForUser(long userId) {
|
|
||||||
ru.soune.nocopy.entity.referral.Referral referral = referralRepository.findByUserId(userId);
|
|
||||||
return referral != null ? referral.getInviterId() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull String getReferralLevelForUser(long userId) {
|
|
||||||
ru.soune.nocopy.entity.referral.Referral referral = referralRepository.findByUserId(userId);
|
|
||||||
return referral != null ? referral.getLevelId() : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Referral getReferralByUserId(long userId) {
|
|
||||||
ru.soune.nocopy.entity.referral.Referral entity = referralRepository.findByUserId(userId);
|
|
||||||
|
|
||||||
return new Referral() {
|
|
||||||
@Override
|
|
||||||
public long getUserId() { return entity.getUserId(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getReferralLink() { return entity.getReferralLink(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getInviter() {
|
|
||||||
return entity.getInviterId() != null ? entity.getInviterId() : 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCurrentLevel() { return entity.getLevelId(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTotalIncome() { return entity.getTotalIncome(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAvailableIncome() {
|
|
||||||
Integer holdBalance = entity.getHoldBalance();
|
|
||||||
return entity.getTotalIncome() - (holdBalance != null ? holdBalance : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHoldBalance() {
|
|
||||||
Integer holdBalance = entity.getHoldBalance();
|
|
||||||
return holdBalance != null ? holdBalance : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean getActive() { return entity.isActive(); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull List<ReferralInvitee> getReferralInvitees(long userId, int pageSize, int pageNumber) {
|
|
||||||
Pageable pageable = PageRequest.of(pageNumber, pageSize);
|
|
||||||
List<ru.soune.nocopy.entity.referral.Referral> referrals = referralRepository.findByInviterId(userId, pageable);
|
|
||||||
|
|
||||||
List<ReferralInvitee> result = new ArrayList<>();
|
|
||||||
|
|
||||||
for (ru.soune.nocopy.entity.referral.Referral referral : referrals) {
|
|
||||||
User user = userRepository.findById(referral.getUserId()).orElse(null);
|
|
||||||
|
|
||||||
String email = user != null ? user.getEmail() : "";
|
|
||||||
boolean isActive = referral.isActive();
|
|
||||||
String regDate = referral.getCreatedAt() != null ?
|
|
||||||
referral.getCreatedAt().toString() : "";
|
|
||||||
|
|
||||||
try {
|
|
||||||
Constructor<ReferralInvitee> constructor = ReferralInvitee.class.getDeclaredConstructor(
|
|
||||||
String.class, boolean.class, String.class
|
|
||||||
);
|
|
||||||
ReferralInvitee invitee = constructor.newInstance(email, isActive, regDate);
|
|
||||||
result.add(invitee);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createReferralEntity(@NotNull Referral entity) {
|
|
||||||
ru.soune.nocopy.entity.referral.Referral referral = new ru.soune.nocopy.entity.referral.Referral();
|
|
||||||
referral.setUserId(entity.getUserId());
|
|
||||||
referral.setReferralLink(entity.getReferralLink());
|
|
||||||
referral.setInviterId(entity.getInviter());
|
|
||||||
referral.setLevelId(entity.getCurrentLevel());
|
|
||||||
referral.setTotalIncome(entity.getTotalIncome());
|
|
||||||
referral.setHoldBalance(entity.getHoldBalance());
|
|
||||||
referral.setActive(entity.getActive());
|
|
||||||
|
|
||||||
referralRepository.save(referral);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void increaseIncome(long userId, int transferAmount) {
|
|
||||||
referralRepository.increaseIncome(userId, transferAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean activateUser(long userId) {
|
|
||||||
try {
|
|
||||||
referralRepository.activateUser(userId);
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getActiveInviteeForUser(long userId) {
|
|
||||||
return referralRepository.countActiveInvitees(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTotalInviteeForUser(long userId) {
|
|
||||||
return referralRepository.countTotalInvitees(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void upgradeReferralLevelForUser(long userId, @NotNull String newLevelId) {
|
|
||||||
referralRepository.updateLevel(userId, newLevelId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package ru.soune.nocopy.service.register;
|
package ru.soune.nocopy.service.register;
|
||||||
|
|
||||||
import jakarta.persistence.EntityNotFoundException;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -11,13 +10,11 @@ import ru.soune.nocopy.dto.register.LoginAnswer;
|
|||||||
import ru.soune.nocopy.dto.register.LoginRequest;
|
import ru.soune.nocopy.dto.register.LoginRequest;
|
||||||
import ru.soune.nocopy.dto.register.RegRequest;
|
import ru.soune.nocopy.dto.register.RegRequest;
|
||||||
import ru.soune.nocopy.entity.company.Company;
|
import ru.soune.nocopy.entity.company.Company;
|
||||||
import ru.soune.nocopy.entity.tarif.Tariff;
|
|
||||||
import ru.soune.nocopy.entity.tarif.TariffInfo;
|
import ru.soune.nocopy.entity.tarif.TariffInfo;
|
||||||
import ru.soune.nocopy.entity.tarif.TariffType;
|
import ru.soune.nocopy.entity.tarif.TariffType;
|
||||||
import ru.soune.nocopy.entity.user.AuthToken;
|
import ru.soune.nocopy.entity.user.AuthToken;
|
||||||
import ru.soune.nocopy.entity.user.Permission;
|
import ru.soune.nocopy.entity.user.Permission;
|
||||||
import ru.soune.nocopy.entity.user.User;
|
import ru.soune.nocopy.entity.user.User;
|
||||||
import ru.soune.nocopy.exception.MaxUserCountIsOver;
|
|
||||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||||
@@ -179,31 +176,20 @@ public class AuthService {
|
|||||||
private void createIndividualCompanyAccount(RegRequest request, User user) {
|
private void createIndividualCompanyAccount(RegRequest request, User user) {
|
||||||
String authToken = request.getAuthToken();
|
String authToken = request.getAuthToken();
|
||||||
Long userIdByToken = authTokenRepository.findUserIdByToken(authToken);
|
Long userIdByToken = authTokenRepository.findUserIdByToken(authToken);
|
||||||
User companyUser = userRepository.findById(userIdByToken)
|
User companyUser = userRepository.findById(userIdByToken).orElseThrow();
|
||||||
.orElseThrow(() -> new EntityNotFoundException("Not found token: " + authToken));
|
|
||||||
|
|
||||||
Company company = companyUser.getCompany();
|
Company company = companyUser.getCompany();
|
||||||
if (company == null) {
|
|
||||||
throw new IllegalStateException("Not have company");
|
|
||||||
}
|
|
||||||
|
|
||||||
TariffInfo tariffInfo = company.getTariffInfo();
|
TariffInfo tariffInfo = company.getTariffInfo();
|
||||||
if (tariffInfo == null) {
|
|
||||||
throw new IllegalStateException("Company not have tariff");
|
|
||||||
}
|
|
||||||
|
|
||||||
Tariff tariff = tariffInfo.getTariff();
|
Long maxUsers = tariffInfo.getTariff().getMaxUsers();
|
||||||
Long maxUsers = tariff.getMaxUsers();
|
|
||||||
long currentCountUsers = userRepository.countByCompanyId(company.getId());
|
long currentCountUsers = userRepository.countByCompanyId(company.getId());
|
||||||
|
|
||||||
if (currentCountUsers >= maxUsers) {
|
//TODO add exception
|
||||||
throw new NotValidFieldException("Max user is over.", new BaseResponse(20002,
|
if (currentCountUsers < maxUsers) {
|
||||||
MessageCode.USER_LIMIT_IS_OVER.getCode(),
|
throw new RuntimeException();
|
||||||
MessageCode.USER_LIMIT_IS_OVER.getDescription(), Map.of(
|
|
||||||
"limit", maxUsers, "countUser", currentCountUsers)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.setCompany(company);
|
user.setCompany(companyUser.getCompany());
|
||||||
user.grantPermission(Permission.DEFAULT_USER_PERMISSIONS);
|
user.grantPermission(Permission.DEFAULT_USER_PERMISSIONS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package ru.soune.nocopy.service.search;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SearchApiToYandexResponseMapper {
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
public YandexSearchResponse mapToYandexResponse(String searchApiJson) throws Exception {
|
||||||
|
JsonNode root = objectMapper.readTree(searchApiJson);
|
||||||
|
|
||||||
|
YandexSearchResponse response = new YandexSearchResponse();
|
||||||
|
List<YandexSearchResponse.ImageResult> imageResults = new ArrayList<>();
|
||||||
|
|
||||||
|
JsonNode visualMatches = root.path("visual_matches");
|
||||||
|
|
||||||
|
if (visualMatches.isArray()) {
|
||||||
|
for (JsonNode match : visualMatches) {
|
||||||
|
YandexSearchResponse.ImageResult result = new YandexSearchResponse.ImageResult();
|
||||||
|
|
||||||
|
|
||||||
|
JsonNode imageNode = match.path("image");
|
||||||
|
if (!imageNode.isMissingNode()) {
|
||||||
|
result.setUrl(imageNode.path("link").asText());
|
||||||
|
result.setWidth(imageNode.path("width").asInt());
|
||||||
|
result.setHeight(imageNode.path("height").asInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
result.setPageUrl(match.path("link").asText());
|
||||||
|
|
||||||
|
result.setPageTitle(match.path("title").asText());
|
||||||
|
|
||||||
|
String source = match.path("source").asText();
|
||||||
|
result.setHost(extractHostFromSource(source));
|
||||||
|
|
||||||
|
if (result.getUrl() != null && !result.getUrl().isEmpty()) {
|
||||||
|
imageResults.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setImages(imageResults);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractHostFromSource(String source) {
|
||||||
|
if (source == null || source.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
source = source.replaceFirst("^(https?://)?(www\\.)?", "");
|
||||||
|
|
||||||
|
int slashIndex = source.indexOf('/');
|
||||||
|
if (slashIndex > 0) {
|
||||||
|
return source.substring(0, slashIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,275 +0,0 @@
|
|||||||
package ru.soune.nocopy.service.search;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
||||||
import jakarta.annotation.PostConstruct;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import okhttp3.*;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import ru.soune.nocopy.dto.BaseResponse;
|
|
||||||
import ru.soune.nocopy.dto.MessageCode;
|
|
||||||
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
|
||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
|
||||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
|
||||||
public class SearchImageService {
|
|
||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
private final OkHttpClient httpClient;
|
|
||||||
|
|
||||||
public SearchImageService() {
|
|
||||||
this.httpClient = new OkHttpClient.Builder()
|
|
||||||
.connectTimeout(5, TimeUnit.SECONDS)
|
|
||||||
.writeTimeout(5, TimeUnit.SECONDS)
|
|
||||||
.readTimeout(15, TimeUnit.SECONDS)
|
|
||||||
.callTimeout(20, TimeUnit.SECONDS)
|
|
||||||
.retryOnConnectionFailure(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
this.objectMapper = new ObjectMapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Value("${yandex.api-key}")
|
|
||||||
private String apiKey;
|
|
||||||
|
|
||||||
@Value("${yandex.folder-id}")
|
|
||||||
private String folderId;
|
|
||||||
|
|
||||||
@Value("${yandex.search-url}")
|
|
||||||
private String searchUrl;
|
|
||||||
|
|
||||||
@Value("${searchapi.api-key:}")
|
|
||||||
private String searchApiKey;
|
|
||||||
|
|
||||||
@Value("${server.baseurl}")
|
|
||||||
private String appBaseUrl;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void init() {
|
|
||||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
|
||||||
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String searchReverseByPublicUrl(FileEntity fileEntity, String engine, String searchType)
|
|
||||||
throws IOException, TimeoutException {
|
|
||||||
|
|
||||||
if (!isImageFile(fileEntity)) {
|
|
||||||
throw new NotValidFieldException(
|
|
||||||
"File not image",
|
|
||||||
new BaseResponse(
|
|
||||||
20007,
|
|
||||||
MessageCode.INVALID_FIELD.getCode(),
|
|
||||||
MessageCode.INVALID_FIELD.getDescription(),
|
|
||||||
Map.of("file_type", fileEntity.getMimeType())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String publicUrl = String.format("%s/api/files/public/%s", appBaseUrl, fileEntity.getId());
|
|
||||||
|
|
||||||
log.info("Searching reverse for image: {}", publicUrl);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return callReverseImageApiByUrl(publicUrl, engine, searchType);
|
|
||||||
} catch (SocketTimeoutException e) {
|
|
||||||
log.error("Yandex search timeout after {}", fileEntity.getId());
|
|
||||||
throw new TimeoutException("Search timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isImageFile(FileEntity fileEntity) {
|
|
||||||
String mimeType = fileEntity.getMimeType();
|
|
||||||
return mimeType != null && mimeType.startsWith("image");
|
|
||||||
}
|
|
||||||
|
|
||||||
private String callReverseImageApiByUrl(String imageUrl, String engine, String searchType) throws IOException {
|
|
||||||
|
|
||||||
if (searchApiKey == null || searchApiKey.isBlank()) {
|
|
||||||
throw new IllegalStateException("SearchAPI key not configured");
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpUrl url = HttpUrl.parse("https://searchapi.io/api/v1/search")
|
|
||||||
.newBuilder()
|
|
||||||
.addQueryParameter("engine", engine)
|
|
||||||
.addQueryParameter("api_key", searchApiKey)
|
|
||||||
.addQueryParameter("url", imageUrl)
|
|
||||||
.addQueryParameter("search_type", searchType)
|
|
||||||
.addQueryParameter("wait", "true")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(url)
|
|
||||||
.header("Accept", "application/json")
|
|
||||||
.header("User-Agent", "Mozilla/5.0")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
try (Response response = httpClient.newCall(request).execute()) {
|
|
||||||
|
|
||||||
long duration = System.currentTimeMillis() - start;
|
|
||||||
log.info("Yandex response code={}, duration={}ms", response.code(), duration);
|
|
||||||
|
|
||||||
if (!response.isSuccessful()) {
|
|
||||||
throw new IOException("API error: " + response.code());
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseBody body = response.body();
|
|
||||||
if (body == null) {
|
|
||||||
throw new IOException("Empty response body");
|
|
||||||
}
|
|
||||||
|
|
||||||
return body.string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<YandexSearchResponse.ImageResult> getAllImagesWithoutPagination(String searchApiJson, String findType)
|
|
||||||
throws IOException {
|
|
||||||
JsonNode root = objectMapper.readTree(searchApiJson);
|
|
||||||
JsonNode matches = root.path(findType);
|
|
||||||
|
|
||||||
List<YandexSearchResponse.ImageResult> allImages = new ArrayList<>();
|
|
||||||
|
|
||||||
if (matches.isArray()) {
|
|
||||||
for (JsonNode match : matches) {
|
|
||||||
YandexSearchResponse.ImageResult result = mapImageResult(match);
|
|
||||||
|
|
||||||
if ("exact_matches".equals(findType)) {
|
|
||||||
JsonNode thumbnail = match.path("thumbnail");
|
|
||||||
if (!thumbnail.isMissingNode() && thumbnail.asText().startsWith("data:image")) {
|
|
||||||
result.setUrl(thumbnail.asText());
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonNode imageNode = match.path("image");
|
|
||||||
if (!imageNode.isMissingNode()) {
|
|
||||||
String directUrl = imageNode.path("link").asText();
|
|
||||||
if (directUrl != null && !directUrl.isBlank()) {
|
|
||||||
result.setUrl(directUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.getUrl() != null && !result.getUrl().isBlank()) {
|
|
||||||
allImages.add(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return allImages;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<YandexSearchResponse.ImageResult> removeDuplicateUrls(
|
|
||||||
List<YandexSearchResponse.ImageResult> yandexImages,
|
|
||||||
List<YandexSearchResponse.ImageResult> googleImages) {
|
|
||||||
|
|
||||||
Map<String, YandexSearchResponse.ImageResult> uniqueByUrl = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
for (YandexSearchResponse.ImageResult image : googleImages) {
|
|
||||||
String normalizedUrl = normalizeUrl(image.getUrl());
|
|
||||||
uniqueByUrl.putIfAbsent(normalizedUrl, image);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (YandexSearchResponse.ImageResult image : yandexImages) {
|
|
||||||
String normalizedUrl = normalizeUrl(image.getUrl());
|
|
||||||
uniqueByUrl.putIfAbsent(normalizedUrl, image);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ArrayList<>(uniqueByUrl.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<YandexSearchResponse.ImageResult> paginateResults(
|
|
||||||
List<YandexSearchResponse.ImageResult> allResults,
|
|
||||||
int page,
|
|
||||||
int pageSize) {
|
|
||||||
|
|
||||||
if (allResults.isEmpty()) {
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
int totalPages = (int) Math.ceil((double) allResults.size() / pageSize);
|
|
||||||
|
|
||||||
if (page > totalPages) {
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
int startIndex = (page - 1) * pageSize;
|
|
||||||
int endIndex = Math.min(startIndex + pageSize, allResults.size());
|
|
||||||
|
|
||||||
return new ArrayList<>(allResults.subList(startIndex, endIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String normalizeUrl(String url) {
|
|
||||||
if (url == null) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (url.startsWith("data:image")) {
|
|
||||||
return url.length() > 100 ? url.substring(0, 100) : url;
|
|
||||||
}
|
|
||||||
|
|
||||||
java.net.URI uri = new java.net.URI(url);
|
|
||||||
String host = uri.getHost();
|
|
||||||
if (host != null) {
|
|
||||||
host = host.toLowerCase().replaceFirst("^www\\.", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
String path = uri.getPath();
|
|
||||||
if (path != null && path.length() > 1 && path.endsWith("/")) {
|
|
||||||
path = path.substring(0, path.length() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (host != null ? host : "") + (path != null ? path : "");
|
|
||||||
} catch (Exception e) {
|
|
||||||
return url.toLowerCase()
|
|
||||||
.replaceFirst("^(https?://)?(www\\.)?", "")
|
|
||||||
.replaceFirst("/$", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private YandexSearchResponse.ImageResult mapImageResult(JsonNode match) {
|
|
||||||
|
|
||||||
YandexSearchResponse.ImageResult result =
|
|
||||||
new YandexSearchResponse.ImageResult();
|
|
||||||
|
|
||||||
JsonNode imageNode = match.path("image");
|
|
||||||
if (imageNode.isObject()) {
|
|
||||||
result.setUrl(imageNode.path("link").asText());
|
|
||||||
result.setWidth(imageNode.path("width").asInt(0));
|
|
||||||
result.setHeight(imageNode.path("height").asInt(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
result.setPageUrl(match.path("link").asText());
|
|
||||||
result.setPageTitle(match.path("title").asText());
|
|
||||||
|
|
||||||
String source = match.path("source").asText();
|
|
||||||
result.setHost(extractHostFromSource(source));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String extractHostFromSource(String source) {
|
|
||||||
if (source == null || source.isEmpty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
source = source.replaceFirst("^(https?://)?(www\\.)?", "");
|
|
||||||
|
|
||||||
int slashIndex = source.indexOf('/');
|
|
||||||
if (slashIndex > 0) {
|
|
||||||
return source.substring(0, slashIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
package ru.soune.nocopy.service.search;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import okhttp3.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.soune.nocopy.dto.BaseResponse;
|
||||||
|
import ru.soune.nocopy.dto.MessageCode;
|
||||||
|
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
||||||
|
import ru.soune.nocopy.entity.file.FileEntity;
|
||||||
|
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class YandexSearchService {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private final OkHttpClient httpClient;
|
||||||
|
|
||||||
|
public YandexSearchService() {
|
||||||
|
this.httpClient = new OkHttpClient.Builder()
|
||||||
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(120, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(30, TimeUnit.SECONDS)
|
||||||
|
.callTimeout(180, TimeUnit.SECONDS)
|
||||||
|
.retryOnConnectionFailure(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.objectMapper = new ObjectMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${yandex.api-key}")
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
@Value("${yandex.folder-id}")
|
||||||
|
private String folderId;
|
||||||
|
|
||||||
|
@Value("${yandex.search-url}")
|
||||||
|
private String searchUrl;
|
||||||
|
|
||||||
|
@Value("${searchapi.api-key:}")
|
||||||
|
private String searchApiKey;
|
||||||
|
|
||||||
|
@Value("${server.baseurl}")
|
||||||
|
private String appBaseUrl;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public YandexSearchResponse searchByFileEntity(FileEntity fileEntity) throws IOException {
|
||||||
|
byte[] fileBytes;
|
||||||
|
|
||||||
|
if (!isImageFile(fileEntity)) {
|
||||||
|
log.error("File not image: {}", fileEntity.getMimeType());
|
||||||
|
throw new NotValidFieldException("File not image", new BaseResponse(20007,
|
||||||
|
MessageCode.INVALID_FIELD.getCode(), MessageCode.INVALID_FIELD.getDescription(),
|
||||||
|
Map.of("file_type", fileEntity.getMimeType())));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fileBytes = readFileFromDisk(fileEntity);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new NotValidFieldException("File not found or cannot read file", new BaseResponse(20007,
|
||||||
|
MessageCode.FILE_NOT_FOUND.getCode(), MessageCode.FILE_NOT_FOUND.getDescription(),
|
||||||
|
Map.of("fileId", fileEntity.getId(),
|
||||||
|
"filePath", fileEntity.getFilePath())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return callYandexApi(fileBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String searchReverseByPublicUrl(FileEntity fileEntity) throws IOException {
|
||||||
|
if (!isImageFile(fileEntity)) {
|
||||||
|
log.error("File not image: {}", fileEntity.getMimeType());
|
||||||
|
throw new NotValidFieldException("File not image", new BaseResponse(20007,
|
||||||
|
MessageCode.INVALID_FIELD.getCode(), MessageCode.INVALID_FIELD.getDescription(),
|
||||||
|
Map.of("file_type", fileEntity.getMimeType())));
|
||||||
|
}
|
||||||
|
|
||||||
|
String publicUrl = String.format("%s/api/files/public/%s", appBaseUrl, fileEntity.getId());
|
||||||
|
|
||||||
|
log.info("Searching reverse for image: {}", publicUrl);
|
||||||
|
|
||||||
|
return callReverseImageApiByUrl(publicUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String callReverseImageApiByUrl(String imageUrl) throws IOException {
|
||||||
|
if (searchApiKey == null || searchApiKey.isEmpty()) {
|
||||||
|
throw new IllegalStateException("SearchAPI key not configured. Set searchapi.api-key property");
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpUrl url = HttpUrl.parse("https://searchapi.io/api/v1/search")
|
||||||
|
.newBuilder()
|
||||||
|
.addQueryParameter("engine", "yandex_reverse_image")
|
||||||
|
.addQueryParameter("api_key", searchApiKey)
|
||||||
|
.addQueryParameter("url", imageUrl)
|
||||||
|
.addQueryParameter("wait", "true")
|
||||||
|
.addQueryParameter("timeout", "30000")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
log.info("Calling Yandex Reverse Image API: {}", url);
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.header("User-Agent", "Mozilla/5.0")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (Response response = httpClient.newCall(request).execute()) {
|
||||||
|
log.info("Response code: {}", response.code());
|
||||||
|
log.info("Response headers: {}", response.headers());
|
||||||
|
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
String errorBody = response.body() != null ? response.body().string() : "No error body";
|
||||||
|
log.error("API request failed: {} - {}", response.code(), errorBody);
|
||||||
|
throw new IOException("API request failed: " + response.code() + " - " + response.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
String responseBody = response.body().string();
|
||||||
|
log.info("Response body length: {} characters", responseBody.length());
|
||||||
|
|
||||||
|
return responseBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isImageFile(FileEntity fileEntity) {
|
||||||
|
String mimeType = fileEntity.getMimeType();
|
||||||
|
return mimeType != null && mimeType.startsWith("image");
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readFileFromDisk(FileEntity fileEntity) throws IOException {
|
||||||
|
Path filePath = Path.of(fileEntity.getFilePath());
|
||||||
|
|
||||||
|
if (!Files.exists(filePath)) {
|
||||||
|
throw new IOException("File not found: " + fileEntity.getFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Files.isReadable(filePath)) {
|
||||||
|
throw new IOException("Cannot read file: " + fileEntity.getFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Files.readAllBytes(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private YandexSearchResponse callYandexApi(byte[] imageBytes) throws IOException {
|
||||||
|
String imageBase64 = Base64.getEncoder().encodeToString(imageBytes);
|
||||||
|
String jsonRequest = String.format("{\"folderId\":\"%s\",\"data\":\"%s\",\"page\":0,\"showSimilarImages\":true}",
|
||||||
|
folderId, imageBase64);
|
||||||
|
|
||||||
|
URL url = new URL(searchUrl);
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
connection.setRequestMethod("POST");
|
||||||
|
connection.setRequestProperty("Authorization", "Api-Key " + apiKey);
|
||||||
|
connection.setRequestProperty("Content-Type", "application/json");
|
||||||
|
connection.setRequestProperty("Accept", "application/json");
|
||||||
|
connection.setConnectTimeout(30000);
|
||||||
|
connection.setReadTimeout(30000);
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
|
||||||
|
try (OutputStream os = connection.getOutputStream()) {
|
||||||
|
byte[] input = jsonRequest.getBytes("utf-8");
|
||||||
|
os.write(input, 0, input.length);
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
|
||||||
|
String responseBody;
|
||||||
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
|
try (BufferedReader br = new BufferedReader(
|
||||||
|
new InputStreamReader(connection.getInputStream(), "utf-8"))) {
|
||||||
|
responseBody = readAll(br);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try (BufferedReader br = new BufferedReader(
|
||||||
|
new InputStreamReader(connection.getErrorStream(), "utf-8"))) {
|
||||||
|
responseBody = readAll(br);
|
||||||
|
}
|
||||||
|
throw new IOException("Error Yandex API: " + responseCode + " - " + responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseJsonResponse(responseBody);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readAll(BufferedReader reader) throws IOException {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
sb.append(line);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private YandexSearchResponse parseJsonResponse(String json) {
|
||||||
|
YandexSearchResponse response = null;
|
||||||
|
try {
|
||||||
|
response = objectMapper.readValue(json, YandexSearchResponse.class);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,7 +35,6 @@ public class TariffInfoService {
|
|||||||
tariffInfo.setStatus(tariffStatus);
|
tariffInfo.setStatus(tariffStatus);
|
||||||
tariffInfo.setEndTariff(endTariff);
|
tariffInfo.setEndTariff(endTariff);
|
||||||
tariffInfo.setTokens(tokens);
|
tariffInfo.setTokens(tokens);
|
||||||
tariffInfo.setBoughtTokens(0);
|
|
||||||
|
|
||||||
return tariffInfoRepository.save(tariffInfo);
|
return tariffInfoRepository.save(tariffInfo);
|
||||||
}
|
}
|
||||||
@@ -53,15 +52,10 @@ public class TariffInfoService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Integer tokens = activeTariffInfo.getTokens();
|
Integer tokens = activeTariffInfo.getTokens();
|
||||||
Integer boughtTokens = activeTariffInfo.getBoughtTokens();
|
|
||||||
|
|
||||||
if (tokens - value >= 0) {
|
if (tokens - value >= 0) {
|
||||||
activeTariffInfo.setTokens(activeTariffInfo.getTokens() - value);
|
activeTariffInfo.setTokens(activeTariffInfo.getTokens() - value);
|
||||||
|
|
||||||
tariffInfoRepository.save(activeTariffInfo);
|
|
||||||
} else if (boughtTokens - value >= 0) {
|
|
||||||
activeTariffInfo.setTokens(activeTariffInfo.getBoughtTokens() - value);
|
|
||||||
|
|
||||||
tariffInfoRepository.save(activeTariffInfo);
|
tariffInfoRepository.save(activeTariffInfo);
|
||||||
} else {
|
} else {
|
||||||
throw new TariffNotFoundException("User not have tokens for protect");
|
throw new TariffNotFoundException("User not have tokens for protect");
|
||||||
@@ -124,8 +118,7 @@ public class TariffInfoService {
|
|||||||
tariffInfo.getStartTariff(),
|
tariffInfo.getStartTariff(),
|
||||||
tariffInfo.getEndTariff(),
|
tariffInfo.getEndTariff(),
|
||||||
tariffInfo.getTariff() != null ? tariffInfo.getTariff().getId() : null,
|
tariffInfo.getTariff() != null ? tariffInfo.getTariff().getId() : null,
|
||||||
tariffInfo.getTariff() != null ? tariffInfo.getTariff().getName() : null,
|
tariffInfo.getTariff() != null ? tariffInfo.getTariff().getName() : null, tariffInfo.getTokens(),
|
||||||
tariffInfo.getTokens() + tariffInfo.getBoughtTokens(),
|
|
||||||
tariffInfo.getTariff().getDiskSize(), 0L, 0,
|
tariffInfo.getTariff().getDiskSize(), 0L, 0,
|
||||||
tariffInfo.getTariff().getMaxFilesCount());
|
tariffInfo.getTariff().getMaxFilesCount());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,44 +101,40 @@ public class TariffInitializationService {
|
|||||||
tariff.setType(type.name());
|
tariff.setType(type.name());
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case FREE:
|
case DEMO:
|
||||||
tariff.setName("free");
|
tariff.setName("demo");
|
||||||
tariff.setPrice(0);
|
tariff.setPrice(demoPrice);
|
||||||
tariff.setTokens(30);
|
tariff.setTokens(demoTokens);
|
||||||
tariff.setMaxFilesCount(10);
|
tariff.setMaxFilesCount(demoMaxFileCount);
|
||||||
tariff.setDiskSize(1024L * 1024 * 100);
|
tariff.setDiskSize(1024L * 1024 * 100);
|
||||||
tariff.setMaxUsers(1L);
|
tariff.setMaxUsers(demoUserCount);
|
||||||
tariff.setTariffAccountType("b2c");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case START:
|
case START:
|
||||||
tariff.setName("start");
|
tariff.setName("start");
|
||||||
tariff.setPrice(1200);
|
tariff.setPrice(startPrice);
|
||||||
tariff.setTokens(300);
|
tariff.setTokens(startTokens);
|
||||||
tariff.setMaxFilesCount(80);
|
tariff.setMaxFilesCount(startMaxFileCount);
|
||||||
tariff.setDiskSize(1024L * 1024 * 500);
|
tariff.setDiskSize(1024L * 1024 * 500);
|
||||||
tariff.setMaxUsers(1L);
|
tariff.setMaxUsers(startUserCount);
|
||||||
tariff.setTariffAccountType("b2c");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PRO:
|
case PRO:
|
||||||
tariff.setName("pro");
|
tariff.setName("pro");
|
||||||
tariff.setPrice(4250);
|
tariff.setPrice(proPrice);
|
||||||
tariff.setTokens(900);
|
tariff.setTokens(proTokens);
|
||||||
tariff.setMaxFilesCount(300);
|
tariff.setMaxFilesCount(proMaxFileCount);
|
||||||
tariff.setDiskSize(1024L * 1024 * 1024 * 10L);
|
tariff.setDiskSize(1024L * 1024 * 1024 * 10L);
|
||||||
tariff.setMaxUsers(1L);
|
tariff.setMaxUsers(proUserCount);
|
||||||
tariff.setTariffAccountType("b2c");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STUDIO:
|
case ENTERPRISE:
|
||||||
tariff.setName("studio");
|
tariff.setName("premium");
|
||||||
tariff.setPrice(9750);
|
tariff.setPrice(enterpricePrice);
|
||||||
tariff.setTokens(3000);
|
tariff.setTokens(enterpriceTokens);
|
||||||
tariff.setMaxFilesCount(1000);
|
tariff.setMaxFilesCount(enterpriceMaxFileCount);
|
||||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||||
tariff.setMaxUsers(2L);
|
tariff.setMaxUsers(enterpriceUserCount);
|
||||||
tariff.setTariffAccountType("b2c");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BASIC:
|
case BASIC:
|
||||||
@@ -148,37 +144,6 @@ public class TariffInitializationService {
|
|||||||
tariff.setMaxFilesCount(enterpriceMaxFileCount);
|
tariff.setMaxFilesCount(enterpriceMaxFileCount);
|
||||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||||
tariff.setMaxUsers(basicUserCount);
|
tariff.setMaxUsers(basicUserCount);
|
||||||
tariff.setTariffAccountType("b2c");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ENTERPRISE:
|
|
||||||
tariff.setName("enterprise");
|
|
||||||
tariff.setPrice(200000);
|
|
||||||
tariff.setTokens(30000);
|
|
||||||
tariff.setMaxFilesCount(100000);
|
|
||||||
tariff.setDiskSize(1024L * 1024 * 1024 * 10L);
|
|
||||||
tariff.setMaxUsers(10L);
|
|
||||||
tariff.setTariffAccountType("b2b");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BUISNES:
|
|
||||||
tariff.setName("buisnes");
|
|
||||||
tariff.setPrice(29000);
|
|
||||||
tariff.setTokens(5000);
|
|
||||||
tariff.setMaxFilesCount(5000);
|
|
||||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
|
||||||
tariff.setMaxUsers(5L);
|
|
||||||
tariff.setTariffAccountType("b2b");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CORPORAT:
|
|
||||||
tariff.setName("corporat");
|
|
||||||
tariff.setPrice(99000);
|
|
||||||
tariff.setTokens(20000);
|
|
||||||
tariff.setMaxFilesCount(50000);
|
|
||||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
|
||||||
tariff.setMaxUsers(10L);
|
|
||||||
tariff.setTariffAccountType("b2b");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import ru.soune.nocopy.dto.tarriff.TariffDTO;
|
import ru.soune.nocopy.dto.tarriff.TariffDTO;
|
||||||
import ru.soune.nocopy.entity.tarif.Tariff;
|
import ru.soune.nocopy.entity.tarif.Tariff;
|
||||||
import ru.soune.nocopy.entity.tarif.TariffType;
|
import ru.soune.nocopy.entity.tarif.TariffType;
|
||||||
import ru.soune.nocopy.entity.user.User;
|
|
||||||
import ru.soune.nocopy.exception.TariffNotFoundException;
|
import ru.soune.nocopy.exception.TariffNotFoundException;
|
||||||
import ru.soune.nocopy.repository.TariffRepository;
|
import ru.soune.nocopy.repository.TariffRepository;
|
||||||
|
|
||||||
@@ -33,15 +32,6 @@ public class TariffService {
|
|||||||
tariffRepository.save(tariff);
|
tariffRepository.save(tariff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TariffDTO> getTariffByAccountType(User user) {
|
|
||||||
String typeAccount = user.canManageCompanySettings() ? "b2b": "b2c";
|
|
||||||
|
|
||||||
return tariffRepository.findByTariffAccountType(typeAccount)
|
|
||||||
.stream()
|
|
||||||
.map(this::convertToDTO)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<TariffDTO> getAllTariffs() {
|
public List<TariffDTO> getAllTariffs() {
|
||||||
return tariffRepository.findAllByOrderByPriceAsc()
|
return tariffRepository.findAllByOrderByPriceAsc()
|
||||||
.stream()
|
.stream()
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ public class FileUtil {
|
|||||||
return FileProtector.Type.VIDEO;
|
return FileProtector.Type.VIDEO;
|
||||||
} else if (mimeType.startsWith("audio")) {
|
} else if (mimeType.startsWith("audio")) {
|
||||||
return FileProtector.Type.AUDIO;
|
return FileProtector.Type.AUDIO;
|
||||||
} else if (mimeType.startsWith("document")) {
|
|
||||||
return FileProtector.Type.DOC;
|
|
||||||
} else {
|
} else {
|
||||||
return FileProtector.Type.IMAGE;
|
return FileProtector.Type.IMAGE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
server:
|
|
||||||
baseurl: "http://localhost"
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
server:
|
|
||||||
baseurl: "http://92.242.61.23"
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
url: jdbc:postgresql://rc1a-7osqmsdaf27d2ieh.mdb.yandexcloud.net:6432/ncp-test?ssl=true&sslmode=require
|
||||||
|
username: ${YC_DB_USER:test}
|
||||||
|
password: ${YC_DB_PASSWORD:testtest}
|
||||||
|
driver-class-name: org.postgresql.Driver # ← ДОБАВЬ
|
||||||
|
|
||||||
|
jpa:
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
||||||
|
|
||||||
|
flyway:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
file:
|
||||||
|
storage:
|
||||||
|
base-path: ${FILE_STORAGE_PATH:/data/uploads}
|
||||||
|
chunk-size: ${FILE_CHUNK_SIZE:1000000} # 1MB
|
||||||
|
max-file-size: ${MAX_FILE_SIZE:10737418240} # 10GB
|
||||||
|
max-retry-attempts: ${MAX_RETRY_ATTEMPTS:3}
|
||||||
|
chunk-timeout-ms: ${CHUNK_TIMEOUT_MS:300000} # 5 минут
|
||||||
|
temp-ttl-hours: ${TEMP_TTL_HOURS:72} # 3 дня
|
||||||
|
auth-token-life-hours: ${AUTH_TOKEN_LIFE:1} # 1 час
|
||||||
|
session-expiry-hours: ${SESSION_EXPIRY_HOURS:24}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
spring:
|
spring:
|
||||||
# config:
|
profiles:
|
||||||
# activate:
|
active: ${SPRING_PROFILES_ACTIVE:local}
|
||||||
# on-profile: ${SPRING_PROFILES_ACTIVE:dev}
|
|
||||||
|
|
||||||
mail:
|
mail:
|
||||||
host: smtp.gmail.com
|
host: smtp.gmail.com
|
||||||
@@ -15,24 +14,15 @@ spring:
|
|||||||
starttls:
|
starttls:
|
||||||
enable: true
|
enable: true
|
||||||
|
|
||||||
# mail:
|
|
||||||
# host: postfix
|
|
||||||
# port: 25
|
|
||||||
# username: noreply@nocopy.com
|
|
||||||
# password: nocopy!nocopy!
|
|
||||||
# properties:
|
|
||||||
# mail.smtp.auth: false
|
|
||||||
# mail.smtp.starttls.enable: false
|
|
||||||
# mail.debug: true
|
|
||||||
|
|
||||||
flyway:
|
flyway:
|
||||||
enabled: true
|
enabled: true
|
||||||
baseline-on-migrate: true
|
baseline-on-migrate: true
|
||||||
|
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:postgresql://${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
# Настройки по умолчанию для local разработки
|
||||||
username: ${POSTGRES_USER}
|
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:54320}/${POSTGRES_DB:no_copy_}
|
||||||
password: ${POSTGRES_PASSWORD}
|
username: ${POSTGRES_USER:postgres}
|
||||||
|
password: ${POSTGRES_PASSWORD:postgres}
|
||||||
driver-class-name: org.postgresql.Driver
|
driver-class-name: org.postgresql.Driver
|
||||||
|
|
||||||
jpa:
|
jpa:
|
||||||
@@ -51,10 +41,6 @@ spring:
|
|||||||
enabled: true
|
enabled: true
|
||||||
resolve-lazily: false
|
resolve-lazily: false
|
||||||
|
|
||||||
dadata:
|
|
||||||
api-key: 0109fcc9d5a0a68c08f3b12ba7e2a620ea438ef7
|
|
||||||
url: "https://suggestions.dadata.ru/suggestions/api/4_1/rs/findById/party"
|
|
||||||
|
|
||||||
file:
|
file:
|
||||||
storage:
|
storage:
|
||||||
base-path: ${FILE_STORAGE_PATH:/data/uploads}
|
base-path: ${FILE_STORAGE_PATH:/data/uploads}
|
||||||
@@ -76,15 +62,6 @@ security:
|
|||||||
server:
|
server:
|
||||||
port: ${SERVER_PORT:8080}
|
port: ${SERVER_PORT:8080}
|
||||||
baseurl: "http://92.242.61.23"
|
baseurl: "http://92.242.61.23"
|
||||||
tomcat:
|
|
||||||
uri-encoding: UTF-8
|
|
||||||
servlet:
|
|
||||||
encoding:
|
|
||||||
charset: UTF-8
|
|
||||||
enabled: true
|
|
||||||
force: true
|
|
||||||
force-request: true
|
|
||||||
force-response: true
|
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
@@ -100,16 +77,9 @@ yandex:
|
|||||||
api-key: ${YANDEX_API_KEY:AQVNyaVaUmgUb1GMCtf5zSEqFxy0woXrcMOOB43q}
|
api-key: ${YANDEX_API_KEY:AQVNyaVaUmgUb1GMCtf5zSEqFxy0woXrcMOOB43q}
|
||||||
folder-id: ${YANDEX_FOLDER_ID:b1gokpdbm6qfpsou8pcd}
|
folder-id: ${YANDEX_FOLDER_ID:b1gokpdbm6qfpsou8pcd}
|
||||||
search-url: ${YANDEX_SEARCH_URL:https://searchapi.api.cloud.yandex.net/v2/image/search_by_image}
|
search-url: ${YANDEX_SEARCH_URL:https://searchapi.api.cloud.yandex.net/v2/image/search_by_image}
|
||||||
cloud:
|
|
||||||
key: "YCAJEmHZcWxzf4oGWvETG3mms"
|
|
||||||
secret-key: "YCPAPZ32XC-LWb8xE0b_xTcBD8NZUCHpSOfu8LUG"
|
|
||||||
region: "ru-central1"
|
|
||||||
s3-endpoint: "https://storage.yandexcloud.net"
|
|
||||||
bucket: "no-copy-storage"
|
|
||||||
|
|
||||||
|
|
||||||
searchapi:
|
searchapi:
|
||||||
api-key: ${SEARCHAPI_API_KEY:rdwNzCr2eybYVbU1KrGTJAgm}
|
api-key: ${SEARCHAPI_API_KEY:2xBChafVmojL7G1ZXzzUuA6o}
|
||||||
reverse-image-url: "https://searchapi.io/api/v1/search"
|
reverse-image-url: "https://searchapi.io/api/v1/search"
|
||||||
|
|
||||||
app:
|
app:
|
||||||
@@ -149,4 +119,3 @@ tarif:
|
|||||||
enterprice-max-files-count: 50
|
enterprice-max-files-count: 50
|
||||||
enterprice-disk-size: 500
|
enterprice-disk-size: 500
|
||||||
enterprice-user-count: 80
|
enterprice-user-count: 80
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user