diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..16f5fd9
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ postgresql
+ true
+ org.postgresql.Driver
+ jdbc:postgresql://localhost:55432/mydatabase
+
+
+
+
+
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index ce1c62c..9de6c56 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -8,6 +8,7 @@
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..c22b6fa
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/debugBackend/build.gradle.kts b/debugBackend/build.gradle.kts
new file mode 100644
index 0000000..152f29e
--- /dev/null
+++ b/debugBackend/build.gradle.kts
@@ -0,0 +1,76 @@
+plugins {
+ kotlin("jvm") version "2.1.10"
+ kotlin("plugin.spring") version "2.1.10"
+ id("org.hibernate.orm") version "6.5.2.Final"
+ id("org.springframework.boot") version "3.4.5"
+ id("io.spring.dependency-management") version "1.1.7"
+ id("org.graalvm.buildtools.native") version "0.10.5"
+}
+
+group = "space.mori.dalbodeule"
+version = "0.5.1"
+
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+}
+
+hibernate {
+ enhancement {
+ enableAssociationManagement.set(false)
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation("org.springframework.boot:spring-boot-starter-actuator")
+ implementation("org.springframework.boot:spring-boot-starter-batch")
+ implementation("org.springframework.boot:spring-boot-starter-data-jpa")
+ implementation("org.springframework.boot:spring-boot-starter-data-redis")
+ implementation("org.springframework.boot:spring-boot-starter-security")
+ implementation("org.jetbrains.kotlin:kotlin-reflect")
+ implementation("org.springframework.boot:spring-boot-starter-websocket")
+ implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.6")
+
+ implementation("io.swagger.core.v3:swagger-core:2.2.30")
+ implementation("io.swagger.core.v3:swagger-annotations:2.2.30")
+
+ implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
+
+ developmentOnly("org.springframework.boot:spring-boot-docker-compose")
+ runtimeOnly("org.postgresql:postgresql:42.7.4")
+
+ implementation("jakarta.xml.bind:jakarta.xml.bind-api:4.0.2")
+ implementation("javax.xml.bind:jaxb-api:2.3.1")
+
+ // HTTP 클라이언트
+ implementation("org.springframework.boot:spring-boot-starter-webflux")
+
+ implementation(rootProject)
+
+ testImplementation("org.springframework.boot:spring-boot-starter-test")
+ testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
+ testImplementation("org.springframework.batch:spring-batch-test")
+ testImplementation("org.springframework.security:spring-security-test")
+ testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+
+ testImplementation(kotlin("test"))
+}
+
+tasks.named("bootRun") {
+ systemProperty("spring.profiles.active", "dev")
+}
+
+kotlin {
+ compilerOptions {
+ freeCompilerArgs.addAll("-Xjsr305=strict")
+ }
+}
+
+tasks.withType {
+ useJUnitPlatform()
+}
diff --git a/debugBackend/compose.yaml b/debugBackend/compose.yaml
new file mode 100644
index 0000000..b2e8c7c
--- /dev/null
+++ b/debugBackend/compose.yaml
@@ -0,0 +1,21 @@
+services:
+ pgvector:
+ image: 'pgvector/pgvector:pg16'
+ environment:
+ - 'POSTGRES_DB=mydatabase'
+ - 'POSTGRES_PASSWORD=secret'
+ - 'POSTGRES_USER=myuser'
+ labels:
+ - "org.springframework.boot.service-connection=postgres"
+ ports:
+ - target: 5432
+ published: 55432
+ protocol: tcp
+ volumes:
+ - postgresql:/var/lib/postgresql/data
+ redis:
+ image: 'redis:latest'
+ ports:
+ - '6379'
+volumes:
+ postgresql:
\ No newline at end of file
diff --git a/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/DebugApplication.kt b/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/DebugApplication.kt
new file mode 100644
index 0000000..b64a8e7
--- /dev/null
+++ b/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/DebugApplication.kt
@@ -0,0 +1,32 @@
+package space.mori.dalbodeule.debug
+
+import io.github.cdimascio.dotenv.dotenv
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.autoconfigure.domain.EntityScan
+import org.springframework.boot.runApplication
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories
+import space.mori.dalbodeule.snapadmin.external.annotations.SnapAdminEnabled
+
+val dotenv = dotenv {
+ ignoreIfMissing = true
+}
+
+@SnapAdminEnabled
+@SpringBootApplication
+@EnableJpaRepositories(basePackages = ["space.mori.dalbodeule.debug.repository"])
+@EntityScan(basePackages = ["space.mori.dalbodeule.debug.model"])
+class DebugApplication
+
+fun main(args: Array) {
+ val envVars = mapOf(
+ "DB_HOST" to dotenv["DB_HOST"],
+ "DB_PORT" to dotenv["DB_PORT"],
+ "DB_NAME" to dotenv["DB_NAME"],
+ "DB_USER" to dotenv["DB_USER"],
+ "DB_PASSWORD" to dotenv["DB_PASSWORD"]
+ )
+
+ runApplication(*args) {
+ setDefaultProperties(envVars)
+ }
+}
\ No newline at end of file
diff --git a/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/config/SecurityConfig.kt b/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/config/SecurityConfig.kt
new file mode 100644
index 0000000..20eed83
--- /dev/null
+++ b/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/config/SecurityConfig.kt
@@ -0,0 +1,43 @@
+package space.mori.dalbodeule.debug.config
+
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
+import org.springframework.security.core.userdetails.User
+import org.springframework.security.core.userdetails.UserDetailsService
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.provisioning.InMemoryUserDetailsManager
+import org.springframework.security.web.SecurityFilterChain
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig {
+
+ @Bean
+ fun filterChain(http: HttpSecurity): SecurityFilterChain {
+ return http
+ .csrf { it.disable() }
+ .authorizeHttpRequests {
+ it.anyRequest().authenticated()
+ }
+ .httpBasic {}
+ .build()
+ }
+
+ @Bean
+ fun userDetailsService(passwordEncoder: PasswordEncoder): UserDetailsService {
+ val admin = User.builder()
+ .username("test@gmail.com")
+ .password(passwordEncoder.encode("password"))
+ .roles("ADMIN")
+ .build()
+ return InMemoryUserDetailsManager(admin)
+ }
+
+ @Bean
+ fun passwordEncoder(): PasswordEncoder {
+ return BCryptPasswordEncoder()
+ }
+}
\ No newline at end of file
diff --git a/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/model/TestTable.kt b/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/model/TestTable.kt
new file mode 100644
index 0000000..99c4d4e
--- /dev/null
+++ b/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/model/TestTable.kt
@@ -0,0 +1,21 @@
+package space.mori.dalbodeule.debug.model
+
+import jakarta.persistence.Column
+import jakarta.persistence.Entity
+import jakarta.persistence.GeneratedValue
+import jakarta.persistence.GenerationType
+import jakarta.persistence.Id
+import jakarta.persistence.Table
+
+@Entity
+@Table(name="test_table")
+data class TestTable(
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ var id: String? = null,
+
+ @Column(nullable = false, length = 32)
+ var name: String
+) {
+ constructor(): this(null, "")
+}
\ No newline at end of file
diff --git a/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/repository/TestTableRepository.kt b/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/repository/TestTableRepository.kt
new file mode 100644
index 0000000..02dfde5
--- /dev/null
+++ b/debugBackend/src/main/kotlin/space/mori/dalbodeule/debug/repository/TestTableRepository.kt
@@ -0,0 +1,7 @@
+package space.mori.dalbodeule.debug.repository
+
+import org.springframework.data.jpa.repository.JpaRepository
+import space.mori.dalbodeule.debug.model.TestTable
+
+interface TestTableRepository: JpaRepository {
+}
\ No newline at end of file
diff --git a/debugBackend/src/main/resources/application.yml b/debugBackend/src/main/resources/application.yml
new file mode 100644
index 0000000..bfd6ccd
--- /dev/null
+++ b/debugBackend/src/main/resources/application.yml
@@ -0,0 +1,18 @@
+spring:
+ datasource:
+ url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}
+ username: ${DB_USER}
+ password: ${DB_PASSWORD}
+ driver-class-name: org.postgresql.Driver
+ jpa:
+ hibernate:
+ ddl-auto: update
+ show-sql: true
+ database-platform: org.hibernate.dialect.PostgreSQLDialect
+snapadmin:
+ enabled: true
+ baseUrl: admin
+ models-package: space.mori.dalbodeule.debug.model
+logging:
+ level:
+ root: INFO
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 3b6483c..3005147 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -5,3 +5,5 @@
*/
rootProject.name = "snap-admin"
+
+include("debugBackend")
\ No newline at end of file
diff --git a/src/main/java/space/mori/dalbodeule/snapadmin/aot/SnapAdminRuntimeHints.java b/src/main/java/space/mori/dalbodeule/snapadmin/aot/SnapAdminRuntimeHints.java
new file mode 100644
index 0000000..b761d73
--- /dev/null
+++ b/src/main/java/space/mori/dalbodeule/snapadmin/aot/SnapAdminRuntimeHints.java
@@ -0,0 +1,135 @@
+package space.mori.dalbodeule.snapadmin.aot;
+
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+import space.mori.dalbodeule.snapadmin.external.SnapAdmin;
+import space.mori.dalbodeule.snapadmin.external.SnapAdminProperties;
+import space.mori.dalbodeule.snapadmin.external.annotations.Disable;
+import space.mori.dalbodeule.snapadmin.external.annotations.DisableEditField;
+import space.mori.dalbodeule.snapadmin.external.annotations.DisplayFormat;
+import space.mori.dalbodeule.snapadmin.external.annotations.DisplayImage; // Assuming this is used
+import space.mori.dalbodeule.snapadmin.external.annotations.HiddenEditForm; // Assuming this is used
+import space.mori.dalbodeule.snapadmin.external.dbmapping.CustomJpaRepository;
+import space.mori.dalbodeule.snapadmin.external.dbmapping.DbObjectSchema;
+import space.mori.dalbodeule.snapadmin.external.dbmapping.fields.*;
+import space.mori.dalbodeule.snapadmin.external.dto.MappingError;
+import space.mori.dalbodeule.snapadmin.external.misc.Utils;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.springframework.aot.hint.MemberCategory.*;
+
+public class SnapAdminRuntimeHints implements RuntimeHintsRegistrar {
+
+ private static final Set> dbFieldTypes = new HashSet<>(Arrays.asList(
+ BooleanFieldType.class, LongFieldType.class, IntegerFieldType.class,
+ BigIntegerFieldType.class, ShortFieldType.class, StringFieldType.class,
+ LocalDateFieldType.class, DateFieldType.class, LocalDateTimeFieldType.class,
+ InstantFieldType.class, FloatFieldType.class, DoubleFieldType.class,
+ BigDecimalFieldType.class, ByteArrayFieldType.class, OffsetDateTimeFieldType.class,
+ ByteFieldType.class, UUIDFieldType.class, CharFieldType.class,
+ EnumFieldType.class, TextFieldType.class
+ // Add any other concrete DbFieldType implementations here
+ ));
+
+ @Override
+ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+ // Register SnapAdmin's own classes
+ hints.reflection().registerType(SnapAdmin.class, INTROSPECT_DECLARED_METHODS, INVOKE_DECLARED_METHODS);
+ hints.reflection().registerType(SnapAdminProperties.class, INVOKE_DECLARED_CONSTRUCTORS, INVOKE_PUBLIC_METHODS); // For Spring binding
+
+ hints.reflection().registerType(DbObjectSchema.class, INVOKE_DECLARED_CONSTRUCTORS, INTROSPECT_DECLARED_METHODS, INVOKE_PUBLIC_METHODS);
+ // CustomJpaRepository 인터페이스 자체는 생성자 호출 힌트가 불필요할 수 있음
+ hints.reflection().registerType(DbField.class, INVOKE_DECLARED_CONSTRUCTORS, INTROSPECT_DECLARED_METHODS, INVOKE_PUBLIC_METHODS);
+ hints.reflection().registerType(MappingError.class, INVOKE_DECLARED_CONSTRUCTORS);
+ // hints.reflection().registerType(Utils.class); // 사용 패턴 확인 후 필요하면 활성화
+
+ // Register DbFieldType and its subclasses for default constructor invocation
+ hints.reflection().registerType(DbFieldType.class);
+ for (Class> dbFieldTypeClass : dbFieldTypes) {
+ hints.reflection().registerType(dbFieldTypeClass, INVOKE_DECLARED_CONSTRUCTORS);
+ }
+ // EnumFieldType has a special constructor too
+ hints.reflection().registerType(EnumFieldType.class, INVOKE_DECLARED_CONSTRUCTORS);
+
+
+ // Register SnapAdmin's custom annotations (and assume their attributes might be read)
+ registerAnnotation(hints, Disable.class);
+ registerAnnotation(hints, DisableEditField.class);
+ registerAnnotation(hints, DisplayFormat.class);
+ registerAnnotation(hints, DisplayImage.class);
+ registerAnnotation(hints, HiddenEditForm.class);
+
+ // Register Jakarta Persistence annotations (and assume their attributes might be read)
+ registerAnnotation(hints, jakarta.persistence.Entity.class);
+ registerAnnotation(hints, jakarta.persistence.Id.class);
+ registerAnnotation(hints, jakarta.persistence.Column.class);
+ registerAnnotation(hints, jakarta.persistence.Lob.class);
+ registerAnnotation(hints, jakarta.persistence.Enumerated.class);
+ registerAnnotation(hints, jakarta.persistence.EnumType.class); // TYPE_VISIBLE 제거
+ registerAnnotation(hints, jakarta.persistence.OneToMany.class);
+ registerAnnotation(hints, jakarta.persistence.ManyToMany.class);
+ registerAnnotation(hints, jakarta.persistence.ManyToOne.class);
+ registerAnnotation(hints, jakarta.persistence.OneToOne.class);
+ registerAnnotation(hints, jakarta.persistence.JoinColumn.class);
+ // Add other JPA annotations if used, e.g. @Table, @Transient
+
+ // Hints for operations on arbitrary (user-defined) @Entity classes
+ // 가능하면 스캔 범위를 제한하거나, 필요한 메서드만 등록
+ // 예시: 특정 패키지 내의 @Entity 클래스 스캔
+ // ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
+ // scanner.addIncludeFilter(new AnnotationTypeFilter(jakarta.persistence.Entity.class));
+ // for (BeanDefinition bd : scanner.findCandidateComponents("com.example.entities")) {
+ // try {
+ // Class> entityClass = Class.forName(bd.getBeanClassName());
+ // hints.reflection().registerType(entityClass, INTROSPECT_DECLARED_FIELDS, INVOKE_DECLARED_METHODS);
+ // } catch (ClassNotFoundException e) {
+ // // Handle exception
+ // }
+ // }
+ // Register SnapAdmin's own classes
+ hints.reflection().registerType(SnapAdmin.class, INTROSPECT_DECLARED_METHODS, INVOKE_DECLARED_METHODS);
+ hints.reflection().registerType(SnapAdminProperties.class, INVOKE_DECLARED_CONSTRUCTORS, INVOKE_PUBLIC_METHODS); // For Spring binding
+
+ hints.reflection().registerType(DbObjectSchema.class, INVOKE_DECLARED_CONSTRUCTORS, INTROSPECT_DECLARED_METHODS, INVOKE_PUBLIC_METHODS);
+ hints.reflection().registerType(CustomJpaRepository.class, INVOKE_DECLARED_CONSTRUCTORS);
+ hints.reflection().registerType(DbField.class, INVOKE_DECLARED_CONSTRUCTORS, INTROSPECT_DECLARED_METHODS, INVOKE_PUBLIC_METHODS);
+ hints.reflection().registerType(MappingError.class, INVOKE_DECLARED_CONSTRUCTORS);
+ hints.reflection().registerType(Utils.class); // If it contains static methods called, or if instantiated
+
+
+ // For Class.forName(className) on unknown classes (typically user entities)
+ // and subsequent operations like getDeclaredFields(), getAnnotation(), newInstance()
+ // This is a general hint. Users should still ensure their entities are hinted.
+ // Consider making this more specific if possible, e.g., by scanning packages if configured.
+ hints.reflection().registerType(Object.class,
+ INTROSPECT_DECLARED_CONSTRUCTORS, INVOKE_DECLARED_CONSTRUCTORS,
+ INTROSPECT_DECLARED_METHODS, INVOKE_DECLARED_METHODS, // For getters/setters if library invokes them
+ DECLARED_FIELDS // For field access
+ );
+
+ // For ClassPathScanningCandidateComponentProvider
+ hints.reflection().registerType(org.springframework.beans.factory.config.BeanDefinition.class, INVOKE_PUBLIC_METHODS); // For getBeanClassName()
+ hints.reflection().registerType(org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.class, INVOKE_DECLARED_CONSTRUCTORS);
+ hints.reflection().registerType(org.springframework.core.type.filter.AnnotationTypeFilter.class, INVOKE_DECLARED_CONSTRUCTORS);
+
+
+ // Resource hints if any .properties or .xml files are loaded from classpath by the library
+ // hints.resources().registerPattern("my-library-config.xml");
+
+ // Proxy hints if JDK proxies are created for library interfaces
+ // hints.proxies().registerJdkProxy(MyLibraryInterface.class);
+
+ // Serialization hints if objects are serialized by the library
+ // hints.serialization().registerType(MySerializableObject.class);
+ }
+
+ private void registerAnnotation(RuntimeHints hints, Class> annotationType) {
+ hints.reflection().registerType(annotationType, INVOKE_DECLARED_METHODS);
+ }
+ private void registerAnnotation(RuntimeHints hints, Class> annotationType, org.springframework.aot.hint.MemberCategory... categories) {
+ hints.reflection().registerType(annotationType, categories);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/space/mori/dalbodeule/snapadmin/external/SnapAdmin.java b/src/main/java/space/mori/dalbodeule/snapadmin/external/SnapAdmin.java
index 428347a..fcc550e 100644
--- a/src/main/java/space/mori/dalbodeule/snapadmin/external/SnapAdmin.java
+++ b/src/main/java/space/mori/dalbodeule/snapadmin/external/SnapAdmin.java
@@ -424,4 +424,4 @@ public class SnapAdmin {
this.authenticated = authenticated;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/space/mori/dalbodeule/snapadmin/external/SnapAdminAutoConfiguration.java b/src/main/java/space/mori/dalbodeule/snapadmin/external/SnapAdminAutoConfiguration.java
index de69527..e670511 100644
--- a/src/main/java/space/mori/dalbodeule/snapadmin/external/SnapAdminAutoConfiguration.java
+++ b/src/main/java/space/mori/dalbodeule/snapadmin/external/SnapAdminAutoConfiguration.java
@@ -1,118 +1,55 @@
-/*
- * SnapAdmin - An automatically generated CRUD admin UI for Spring Boot apps
- * Copyright (C) 2023 Ailef (http://ailef.tech)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
package space.mori.dalbodeule.snapadmin.external;
-import java.util.Properties;
-
-import javax.sql.DataSource;
-
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.boot.jdbc.DataSourceBuilder;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
-import org.springframework.orm.jpa.JpaTransactionManager;
-import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
-import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
-import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.annotation.EnableTransactionManagement;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.transaction.support.TransactionTemplate;
-
+import space.mori.dalbodeule.snapadmin.external.controller.DataExportController;
+import space.mori.dalbodeule.snapadmin.external.controller.FileDownloadController;
+import space.mori.dalbodeule.snapadmin.external.controller.GlobalController;
+import space.mori.dalbodeule.snapadmin.external.controller.SnapAdminController;
+import space.mori.dalbodeule.snapadmin.external.controller.rest.AutocompleteController;
+import space.mori.dalbodeule.snapadmin.external.dbmapping.CustomJpaRepository;
+import space.mori.dalbodeule.snapadmin.external.dbmapping.DbObjectSchema;
+import space.mori.dalbodeule.snapadmin.external.dbmapping.SnapAdminRepository;
import space.mori.dalbodeule.snapadmin.internal.InternalSnapAdminConfiguration;
+import space.mori.dalbodeule.snapadmin.internal.UserConfiguration;
+import space.mori.dalbodeule.snapadmin.internal.service.ConsoleQueryService;
+import space.mori.dalbodeule.snapadmin.internal.service.UserActionService;
+import space.mori.dalbodeule.snapadmin.internal.service.UserSettingsService;
/**
- * The configuration class for "internal" data source. This is not the
- * source connected to the user's data/entities, but rather an internal
- * H2 database which is used by SnapAdmin to store user
- * settings and other information like operations history.
+ * SnapAdmin 자동 설정 클래스. 메인 애플리케이션의 JPA 설정을 재사용합니다.
*/
-@ConditionalOnProperty(name = "snapadmin.enabled", matchIfMissing = false)
-@ComponentScan
-@EnableConfigurationProperties(SnapAdminProperties.class)
@Configuration
-@EnableJpaRepositories(
- entityManagerFactoryRef = "internalEntityManagerFactory",
- basePackages = { "space.mori.dalbodeule.snapadmin.internal.repository" }
-)
-@EnableTransactionManagement
-@Import(InternalSnapAdminConfiguration.class)
+@ConditionalOnProperty(name = "snapadmin.enabled", havingValue = "true", matchIfMissing = false)
+@EnableConfigurationProperties(SnapAdminProperties.class)
+@EnableJpaRepositories(basePackages = "space.mori.dalbodeule.snapadmin.internal.repository")
+@EntityScan(basePackages = "space.mori.dalbodeule.snapadmin.internal.model")
+@Import({
+ SnapAdmin.class,
+ SnapAdminMvcConfig.class,
+ StartupAuthCheckRunner.class,
+ ThymeleafUtils.class,
+
+ // controllers
+ SnapAdminController.class,
+ DataExportController.class,
+ FileDownloadController.class,
+ GlobalController.class,
+ AutocompleteController.class,
+
+ // dbmapping
+ SnapAdminRepository.class,
+
+ // internals
+ ConsoleQueryService.class,
+ UserActionService.class,
+ UserSettingsService.class,
+ InternalSnapAdminConfiguration.class,
+ UserConfiguration.class
+})
public class SnapAdminAutoConfiguration {
- @Autowired
- private SnapAdminProperties props;
-
- /**
- * Builds and returns the internal data source.
- *
- * @return
- */
- @Bean
- DataSource internalDataSource() {
- DataSourceBuilder> dataSourceBuilder = DataSourceBuilder.create();
- dataSourceBuilder.driverClassName("org.h2.Driver");
- if (props.isTestMode()) {
- dataSourceBuilder.url("jdbc:h2:mem:test");
- } else {
- dataSourceBuilder.url("jdbc:h2:file:./snapadmin_internal");
- }
-
- dataSourceBuilder.username("sa");
- dataSourceBuilder.password("password");
- return dataSourceBuilder.build();
- }
-
- @Bean
- LocalContainerEntityManagerFactoryBean internalEntityManagerFactory() {
- LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
- factoryBean.setDataSource(internalDataSource());
- factoryBean.setPersistenceUnitName("internal");
- factoryBean.setPackagesToScan("space.mori.dalbodeule.snapadmin.internal.model");
- factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
- Properties properties = new Properties();
- properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
- properties.setProperty("hibernate.hbm2ddl.auto", "update");
- factoryBean.setJpaProperties(properties);
- factoryBean.afterPropertiesSet();
- return factoryBean;
- }
-
- /**
- * The internal transaction manager. It is not defined as a bean
- * in order to avoid "colliding" with the default transactionManager
- * registered by the user. Internally, we use this to instantiate a
- * TransactionTemplate and run all transactions manually instead of
- * relying on the @link {@link Transactional} annotation.
- * @return
- */
- PlatformTransactionManager internalTransactionManager() {
- JpaTransactionManager transactionManager = new JpaTransactionManager();
- transactionManager.setEntityManagerFactory(internalEntityManagerFactory().getObject());
- return transactionManager;
- }
-
- @Bean
- TransactionTemplate internalTransactionTemplate() {
- return new TransactionTemplate(internalTransactionManager());
- }
-
}
\ No newline at end of file
diff --git a/src/main/java/space/mori/dalbodeule/snapadmin/external/annotations/SnapAdminEnabled.java b/src/main/java/space/mori/dalbodeule/snapadmin/external/annotations/SnapAdminEnabled.java
new file mode 100644
index 0000000..5083cfd
--- /dev/null
+++ b/src/main/java/space/mori/dalbodeule/snapadmin/external/annotations/SnapAdminEnabled.java
@@ -0,0 +1,14 @@
+package space.mori.dalbodeule.snapadmin.external.annotations;
+
+import org.springframework.context.annotation.Import;
+import space.mori.dalbodeule.snapadmin.external.SnapAdminAutoConfiguration;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Import(SnapAdminAutoConfiguration.class) // SnapAdmin 설정 클래스를 Import
+public @interface SnapAdminEnabled {
+ // 필요한 속성이 있다면 정의
+}
\ No newline at end of file
diff --git a/src/main/java/space/mori/dalbodeule/snapadmin/external/dbmapping/DbObjectSchema.java b/src/main/java/space/mori/dalbodeule/snapadmin/external/dbmapping/DbObjectSchema.java
index 12ca282..1fc0807 100644
--- a/src/main/java/space/mori/dalbodeule/snapadmin/external/dbmapping/DbObjectSchema.java
+++ b/src/main/java/space/mori/dalbodeule/snapadmin/external/dbmapping/DbObjectSchema.java
@@ -35,6 +35,7 @@ import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.Entity;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
@@ -99,6 +100,10 @@ public class DbObjectSchema {
* @param snapAdmin the SnapAdmin instance
*/
public DbObjectSchema(Class> klass, SnapAdmin snapAdmin) {
+ if (klass.getAnnotation(Entity.class) == null) {
+ throw new SnapAdminException("Class " + klass.getName() + " is not an @Entity");
+ }
+
this.snapAdmin = snapAdmin;
this.entityClass = klass;
diff --git a/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/ConsoleQuery.java b/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/ConsoleQuery.java
index c4ec1d1..2228de8 100644
--- a/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/ConsoleQuery.java
+++ b/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/ConsoleQuery.java
@@ -22,19 +22,20 @@ package space.mori.dalbodeule.snapadmin.internal.model;
import java.time.LocalDateTime;
+import jakarta.persistence.*;
import org.hibernate.annotations.UuidGenerator;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Id;
-import jakarta.persistence.Lob;
+import space.mori.dalbodeule.snapadmin.external.annotations.Disable;
@Entity
+@Disable
+@Table(name="snapadmin_console_query")
public class ConsoleQuery {
@Id
@UuidGenerator
private String id;
- @Lob
+ @Column(columnDefinition = "TEXT")
private String sql;
private String title;
diff --git a/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/UserAction.java b/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/UserAction.java
index 7ced690..a7bb9d9 100644
--- a/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/UserAction.java
+++ b/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/UserAction.java
@@ -21,14 +21,10 @@ package space.mori.dalbodeule.snapadmin.internal.model;
import java.time.LocalDateTime;
+import jakarta.persistence.*;
import org.springframework.format.datetime.standard.DateTimeFormatterFactory;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.Lob;
+import space.mori.dalbodeule.snapadmin.external.annotations.Disable;
/**
* An write operation executed by a user from the web UI. This class
@@ -36,6 +32,8 @@ import jakarta.persistence.Lob;
* concrete yet (e.g. a diff or SQL query) about what change was performed.
*/
@Entity
+@Disable
+@Table(name="snapadmin_user_action")
public class UserAction {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -51,8 +49,7 @@ public class UserAction {
* The SQL query generated by the operation.
* This field is here but it's NOT currently supported
*/
- @Lob
- @Column(nullable = false)
+ @Column(nullable = false, columnDefinition = "TEXT")
private String sql;
/**
diff --git a/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/UserSetting.java b/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/UserSetting.java
index d8b855e..b3b1027 100644
--- a/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/UserSetting.java
+++ b/src/main/java/space/mori/dalbodeule/snapadmin/internal/model/UserSetting.java
@@ -21,11 +21,15 @@ package space.mori.dalbodeule.snapadmin.internal.model;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import space.mori.dalbodeule.snapadmin.external.annotations.Disable;
/**
* A single variable in the user settings.
*/
@Entity
+@Disable
+@Table(name="snapadmin_user_setting")
public class UserSetting {
/**
* The id of the variable (its name)
diff --git a/src/main/java/space/mori/dalbodeule/snapadmin/internal/repository/CustomActionRepositoryImpl.java b/src/main/java/space/mori/dalbodeule/snapadmin/internal/repository/CustomActionRepositoryImpl.java
index f506265..7503cde 100644
--- a/src/main/java/space/mori/dalbodeule/snapadmin/internal/repository/CustomActionRepositoryImpl.java
+++ b/src/main/java/space/mori/dalbodeule/snapadmin/internal/repository/CustomActionRepositoryImpl.java
@@ -39,8 +39,6 @@ import space.mori.dalbodeule.snapadmin.internal.model.UserAction;
*/
@Component
public class CustomActionRepositoryImpl implements CustomActionRepository {
-
- @PersistenceContext(unitName = "internal")
private EntityManager entityManager;
/**
diff --git a/src/main/resources/META-INF/spring/org.springframework.aot.hint.RuntimeHintsRegistrar.properties b/src/main/resources/META-INF/spring/org.springframework.aot.hint.RuntimeHintsRegistrar.properties
new file mode 100644
index 0000000..28d3d16
--- /dev/null
+++ b/src/main/resources/META-INF/spring/org.springframework.aot.hint.RuntimeHintsRegistrar.properties
@@ -0,0 +1 @@
+space.mori.dalbodeule.snapadmin.aot.SnapAdminRuntimeHints=space.mori.dalbodeule.snapadmin.aot.SnapAdminRuntimeHints
\ No newline at end of file