Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ build/
.air/

.config-repo/
.config/
.properties/

.env

application-*.properties
application-*.yml
application-*.yaml
docker-compose.dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.theurl.bundle.persistence.handler;

import com.neroyun.mediator.Handler;
import io.theurl.bundle.persistence.model.BundleDetailModel;
import io.theurl.bundle.persistence.query.BundleDetailQuery;
import io.theurl.bundle.persistence.repository.JpaBundleRepository;
import io.theurl.framework.core.BeanScope;
import jakarta.persistence.EntityNotFoundException;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;

@Component
@Scope(BeanScope.PROTOTYPE)
public class BundleDetailQueryHandler implements Handler<BundleDetailQuery, BundleDetailModel> {

private final JpaBundleRepository repository;
private final ModelMapper mapper;

public BundleDetailQueryHandler(JpaBundleRepository repository, ModelMapper mapper) {
this.repository = repository;
this.mapper = mapper;
}

@Override
public CompletableFuture<BundleDetailModel> handleAsync(BundleDetailQuery message) {
var entity = repository.findByVanity(message.vanity())
.orElse(null);
if (entity == null || entity.isDeleted()) {
throw new EntityNotFoundException("Bundle not found with vanity: " + message.vanity());
}

var detail = mapper.map(entity, BundleDetailModel.class);
return CompletableFuture.completedFuture(detail);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.theurl.bundle.persistence.model;

import lombok.Data;

import java.time.LocalDateTime;

@Data
public class BundleDetailModel {
private Long id;
private String vanity;
private String type;
private String name;
private String description;
private Long ownerId;
private String ownerName;
private int itemCount;
private int commentCount;
private int favoriteCount;
private int visitCount;
private LocalDateTime lastVisitedAt;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.theurl.bundle.persistence.model;

public class BundleItemModel {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.theurl.bundle.persistence.query;

import com.neroyun.mediator.Query;
import io.theurl.bundle.persistence.model.BundleDetailModel;

public record BundleDetailQuery(String vanity) implements Query<BundleDetailModel> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.theurl.bundle.persistence.repository;

import io.theurl.bundle.domain.aggregate.Bundle;
import io.theurl.bundle.domain.repository.BundleRepository;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;

@Repository
public class BundleRepositoryImpl implements BundleRepository {
private final JpaBundleRepository repository;
private final ModelMapper mapper;

public BundleRepositoryImpl(JpaBundleRepository repository, ModelMapper mapper) {
this.repository = repository;
this.mapper = mapper;
}

@Override
public void save(Bundle bundle, long operatorId) {
var entity = repository.findById(bundle.getId())
.orElse(null);
if (entity == null) {
entity = mapper.map(bundle, io.theurl.bundle.persistence.entity.Bundle.class);
entity.setCreatedBy(operatorId);
} else if (bundle.isDeleted()) {
entity.setDeleted(true);
entity.setDeletedBy(operatorId);
entity.setDeletedAt(LocalDateTime.now());
} else {
mapper.map(bundle, entity);
entity.setUpdatedBy(operatorId);
}

repository.save(entity);
}

@Override
public Bundle findById(Long id) {
var entity = repository.findById(id).orElse(null);
if (entity == null) {
return null;
}
return mapper.map(entity, Bundle.class);
}

@Override
public Bundle findByVanity(String vanity) {
var entity = repository.findByVanity(vanity)
.orElse(null);
if (entity == null || entity.isDeleted()) {
return null;
}
return mapper.map(entity, Bundle.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.theurl.bundle.persistence.repository;

import io.theurl.bundle.persistence.entity.Bundle;
import org.springframework.data.repository.CrudRepository;

import java.util.Optional;

public interface JpaBundleRepository extends CrudRepository<Bundle, Long> {
Optional<Bundle> findByVanity(String vanity);
}
5 changes: 5 additions & 0 deletions config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>3.0.3</version>
</dependency>
</dependencies>

<build>
Expand Down
54 changes: 54 additions & 0 deletions config/src/main/java/io/theurl/config/ConfigController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.theurl.config;

import org.bson.Document;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("api")
public class ConfigController {
private final MongoTemplate mongo;
private final String collectionName;

public ConfigController(MongoTemplate mongo, @Value("${spring.cloud.config.server.mongodb.collection:properties}") String collectionName) {
this.mongo = mongo;
this.collectionName = collectionName;
}

@PostMapping("{application}/{profile}")
public void update(@PathVariable String application, @PathVariable String profile, @RequestBody Document payload) {

if (application == null || application.isEmpty()) {
throw new IllegalArgumentException("application cannot be null or empty");
}

if (profile == null || profile.isEmpty()) {
throw new IllegalArgumentException("profile cannot be null or empty");
}

var query = new Query();
query.addCriteria(Criteria.where("application").is(application)
.and("profile").is(profile));
var update = new Update();
update.set("version", System.currentTimeMillis());
update.set("properties", payload);
mongo.upsert(query, update, collectionName);
}

@GetMapping("{application}/{profile}")
public Object query(@PathVariable String application, @PathVariable String profile) {
var query = new Query();
query.addCriteria(Criteria.where("application").is(application).and("profile").is(profile));
var document = mongo.find(query, Document.class, collectionName).stream().findFirst().orElse(null);
if (document == null) {
return null;
}

return document.get("properties");
}
}
50 changes: 50 additions & 0 deletions config/src/main/java/io/theurl/config/LoggingRequestFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.theurl.config;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jspecify.annotations.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class LoggingRequestFilter extends OncePerRequestFilter {

private static final String[] ipHeaders = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_CLIENT_IP",
"HTTP_X_FORWARDED_FOR"
};

@Override
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
var ip = getClientIp(request);
String method = request.getMethod();
String uri = request.getRequestURI();
String queryString = request.getQueryString();
String fullUri = uri + (queryString != null ? "?" + queryString : "");
System.out.println(request.getSession().getId() + " " + ip + " : " + method + " " + fullUri);
filterChain.doFilter(request, response);
System.out.println(request.getSession().getId() + " " + ip + " : " + response.getStatus());
}

public static String getClientIp(HttpServletRequest request) {
for (String header : ipHeaders) {
if (request.getHeader(header) != null) {
return request.getHeader(header);
}
}

var remoteAddr = request.getRemoteAddr();
return switch (remoteAddr) {
case null -> "127.0.0.1";
case "0:0:0:0:0:0:0:1", "::1" -> "127.0.0.1";
default -> remoteAddr;
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.theurl.config;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RequestFilterConfiguration {

@Bean
public FilterRegistrationBean<LoggingRequestFilter> loggingFilterRegistration() {
FilterRegistrationBean<LoggingRequestFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingRequestFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) {
http.authorizeHttpRequests(auth -> {
auth.requestMatchers("/api/**").authenticated();
//auth.requestMatchers("/api/**").authenticated();
auth.anyRequest().permitAll();
})
.csrf(AbstractHttpConfigurer::disable)
Expand Down
5 changes: 3 additions & 2 deletions config/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ spring:
name: ${CONFIG_SERVER_USERNAME:config-user}
password: ${CONFIG_SERVER_PASSWORD:config-password}
mongodb:
uri: ${MONGO_URI:mongodb://host.docker.internal:27017/linkyou?authSource=admin}
uri: ${MONGO_URI:mongodb://mongoadmin:nerosoft.8888@localhost:27017/linkyou?authSource=admin}
data:
redis:
url: ${REDIS_URL:redis://localhost:6379}
url: ${REDIS_URL:redis://host.docker.internal:6379}
cloud:
config:
server:
prefix: env
native:
search-locations: ${CONFIG_SEARCH_LOCATIONS:file:./config}
mongodb:
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ services:
- CONFIG_SERVER_USERNAME=theurl
- CONFIG_SERVER_PASSWORD=Qwer.1234
- CONFIG_SEARCH_LOCATIONS=file:./config
- MONGO_URI=mongodb://theurl-mongo:27017/linkyou?authSource=admin
- REDIS_URL=redis://theurl-redis:6379
volumes:
- theurl-config-data:/app/config

Expand All @@ -18,9 +20,7 @@ services:
ports:
- "8901:8901"
environment:
- CONFIG_SERVER_URI=http://theurl-config-server:8900
- CONFIG_SERVER_USERNAME=theurl
- CONFIG_SERVER_PASSWORD=Qwer.1234
- CONFIG_SERVER_URI=http://theurl-config-server:8900/env
- DB_URL=jdbc:postgresql://theurl-db:5432/linkyou?currentSchema=public
- DB_USERNAME=postgres
- DB_PASSWORD=postgres
Expand Down
6 changes: 2 additions & 4 deletions identity/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@ WORKDIR /app

ENV JAVA_OPTS="-Xms512m -Xmx1024m"
ENV SPRING_PROFILES_ACTIVE=prod
ENV CONFIG_SERVER_URI=http://theurl-config-server:8900
ENV CONFIG_SERVER_USERNAME=theurl
ENV CONFIG_SERVER_PASSWORD=Qwer.1234
ENV DB_URL=jdbc:postgresql://theurl-db:5432/linkyou?currentSchema=public
ENV CONFIG_SERVER_URI=http://theurl-config-server:8900/env
ENV DB_URL="jdbc:postgresql://theurl-db:5432/linkyou?currentSchema=public"
ENV DB_USERNAME=postgres
ENV DB_PASSWORD=postgres
ENV DB_DRIVER=org.postgresql.Driver
Expand Down
8 changes: 1 addition & 7 deletions identity/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ spring:
application:
name: identity
config:
import: optional:file:.env[.properties]
cloud:
config:
enabled: true
uri: ${CONFIG_SERVER_URI:http://localhost:8900}
password: ${CONFIG_SERVER_PASSWORD:Qwer.1234}
username: ${CONFIG_SERVER_USERNAME:theurl}
import: optional:configserver:${CONFIG_SERVER_URI:http://localhost:8900/env}
datasource:
url: ${DB_URL:jdbc:postgresql://localhost:5432/linkyou?currentSchema=public}
username: ${DB_USERNAME:postgres}
Expand Down
Loading