diff --git a/.dockerignore b/.dockerignore
index 94810d0..b6a660c 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,5 +1,13 @@
*
-!target/*-runner
-!target/*-runner.jar
-!target/lib/*
-!target/quarkus-app/*
\ No newline at end of file
+!network-backend
+!network-context
+!network-controller-rest
+!network-core
+!network-data-jooq
+!network-data-panache
+!network-domain
+!network-exception
+!network-schema-rest
+!mvnw
+!.mvn
+!pom.xml
\ No newline at end of file
diff --git a/.env b/.env
new file mode 100644
index 0000000..87c0046
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+DATASOURCE_PASSWORD=123456789
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c745233..1c716f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,3 +76,12 @@ Network Trash Folder
Temporary Items
.apdisk
/social-network.iml
+/network-backend.iml
+/network-context.iml
+/network-controller-rest.iml
+/network-core.iml
+/network-data-jooq.iml
+/network-data-panache.iml
+/network-domain.iml
+/network-schema-rest.iml
+/network-exception.iml
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..c229c52
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,48 @@
+FROM eclipse-temurin:21 as app-build
+ENV RELEASE=21
+
+WORKDIR /opt/build
+COPY .mvn .
+COPY pom.xml .
+COPY network-backend/pom.xml ./network-backend/pom.xml
+COPY network-context/pom.xml ./network-context/pom.xml
+COPY network-controller-rest/pom.xml ./network-controller-rest/pom.xml
+COPY network-core/pom.xml ./network-core/pom.xml
+COPY network-data-jooq/pom.xml ./network-data-jooq/pom.xml
+COPY network-data-panache/pom.xml ./network-data-panache/pom.xml
+COPY network-domain/pom.xml ./network-domain/pom.xml
+COPY network-exception/pom.xml ./network-exception/pom.xml
+COPY network-schema-rest/pom.xml ./network-schema-rest/pom.xml
+COPY . .
+RUN ./mvnw -T 1C dependency:go-offline
+RUN ./mvnw -T 1C -Dquarkus.package.type=uber-jar clean package && mv network-backend/target/ ./uber-jar && ./mvnw -T 1C clean package && mv network-backend/target/quarkus-app/* . && mv network-backend/target/*.jar .
+RUN $JAVA_HOME/bin/jlink \
+ --add-modules `jdeps --ignore-missing-deps -q -recursive --multi-release ${RELEASE} --print-module-deps --class-path 'lib/boot/*:lib/main/*:quarkus/*:app/*:*.jar' uber-jar/*.jar`,jdk.zipfs,jdk.crypto.cryptoki \
+ --strip-java-debug-attributes \
+ --no-man-pages \
+ --no-header-files \
+ --compress=2 \
+ --output jdk
+RUN rm -rf /opt/build/.mvn /opt/build/*.xml /opt/build/network-*/ /opt/build/uber-jar
+
+FROM debian:buster-slim
+
+ARG BUILD_PATH=/opt/build
+ENV JAVA_HOME=/quarkus-app/jdk
+ENV PATH "${JAVA_HOME}/bin:${PATH}"
+
+RUN groupadd --gid 1000 quarkus-app && useradd --uid 1000 --gid quarkus-app --shell /bin/bash --create-home quarkus-app
+USER quarkus-app:quarkus-app
+
+WORKDIR /quarkus-app
+
+COPY --from=app-build --chown=quarkus-app:quarkus-app $BUILD_PATH/jdk/ ./jdk/
+COPY --from=app-build --chown=quarkus-app:quarkus-app $BUILD_PATH/lib/ ./lib/
+COPY --from=app-build --chown=quarkus-app:quarkus-app $BUILD_PATH/*.jar ./
+COPY --from=app-build --chown=quarkus-app:quarkus-app $BUILD_PATH/app/ ./app/
+COPY --from=app-build --chown=quarkus-app:quarkus-app $BUILD_PATH/quarkus/ ./quarkus/
+
+EXPOSE 8080 8090
+
+ENTRYPOINT ["java", "-jar", "-Dfile.encoding=UTF8", "-Dconsole.encoding=UTF8", "-Dorg.jooq.no-tips=true", "-Dorg.jooq.no-logo=true", "/quarkus-app/quarkus-run.jar"]
+
diff --git a/src/main/docker/Dockerfile.native-micro b/Dockerfile.native
similarity index 67%
rename from src/main/docker/Dockerfile.native-micro
rename to Dockerfile.native
index 1082dd3..c147576 100644
--- a/src/main/docker/Dockerfile.native-micro
+++ b/Dockerfile.native
@@ -1,20 +1,20 @@
####
-# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
+# This Dockerfile.native is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
# It uses a micro base image, tuned for Quarkus native executables.
# It reduces the size of the resulting container image.
# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
#
# Before building the container image run:
#
-# ./mvnw package -Dnative
+# ./mvnw package -Dnative -Dquarkus.native.remote-container-build=true
#
# Then, build the image with:
#
-# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/social-network .
+# docker build -t quarkus/social-network:latest .
#
# Then run the container using:
#
-# docker run -i --rm -p 8080:8080 quarkus/social-network
+# docker run -i --rm -p 8080:8080 quarkus/social-network:latest
#
###
FROM quay.io/quarkus/quarkus-micro-image:2.0
diff --git a/README.md b/README.md
index 07b1641..82b59fd 100644
--- a/README.md
+++ b/README.md
@@ -1,68 +1,8 @@
-# social-network
+# MySpace
-This project uses Quarkus, the Supersonic Subatomic Java Framework.
-
-If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ .
-
-## Running the application in dev mode
-
-You can run your application in dev mode that enables live coding using:
-
-```shell script
-./mvnw compile quarkus:dev
+## Запуск
+```
+docker compose up --build
```
-> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/.
-
-## Packaging and running the application
-
-The application can be packaged using:
-
-```shell script
-./mvnw package
-```
-
-It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory.
-Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory.
-
-The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`.
-
-If you want to build an _über-jar_, execute the following command:
-
-```shell script
-./mvnw package -Dquarkus.package.type=uber-jar
-```
-
-The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`.
-
-## Creating a native executable
-
-You can create a native executable using:
-
-```shell script
-./mvnw package -Dnative
-```
-
-Or, if you don't have GraalVM installed, you can run the native executable build in a container using:
-
-```shell script
-./mvnw package -Dnative -Dquarkus.native.container-build=true
-```
-
-You can then execute your native executable with: `./target/social-network-1.0-SNAPSHOT-runner`
-
-If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.
-
-## Related Guides
-
-- Hibernate Validator ([guide](https://quarkus.io/guides/validation)): Validate object properties (field, getter) and method parameters for your beans (REST, CDI, Jakarta Persistence)
-- Reactive PostgreSQL client ([guide](https://quarkus.io/guides/reactive-sql-clients)): Connect to the PostgreSQL database using the reactive pattern
-- Micrometer metrics ([guide](https://quarkus.io/guides/micrometer)): Instrument the runtime and your application with dimensional metrics using Micrometer.
-
-## Provided Code
-
-### RESTEasy Reactive
-
-Easily start your Reactive RESTful Web Services
-
-[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources)
+Postman коллекция находится в папке `postman`.
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..444167a
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,39 @@
+services:
+
+ myspace-database:
+ image: postgres:15-alpine
+ hostname: myspace-database
+ container_name: myspace-database
+ networks:
+ myspace-database:
+ ports:
+ - 5432:5432
+ environment:
+ POSTGRES_DB: myspace
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: ${DATASOURCE_PASSWORD}
+ volumes:
+ - myspace-database:/var/lib/postgresql/data/
+
+ myspace-backend:
+ build: .
+ image: myspace-backend:jvm
+ hostname: myspace-backend
+ container_name: myspace-backend
+ restart: always
+ ports:
+ - 8080:8080
+ networks:
+ myspace-database:
+ depends_on:
+ - myspace-database
+ environment:
+ DB_URL: postgresql://myspace-database:5432/myspace
+ DB_USERNAME: postgres
+ DB_PASSWORD: ${DATASOURCE_PASSWORD}
+
+volumes:
+ myspace-database:
+
+networks:
+ myspace-database:
\ No newline at end of file
diff --git a/network-backend/pom.xml b/network-backend/pom.xml
new file mode 100644
index 0000000..cfca360
--- /dev/null
+++ b/network-backend/pom.xml
@@ -0,0 +1,93 @@
+
+
+ 4.0.0
+
+ dev.struchkov.network
+ social-network
+ 1.0.0-SNAPSHOT
+
+
+ network-backend
+
+
+
+ dev.struchkov.network
+ network-core
+
+
+ dev.struchkov.network
+ network-data-panache
+
+
+
+
+
+
+ dev.struchkov.network
+ network-controller-rest
+
+
+
+ io.quarkus
+ quarkus-liquibase
+
+
+ org.postgresql
+ postgresql
+
+
+ io.quarkus
+ quarkus-arc
+
+
+ io.quarkus
+ quarkus-micrometer
+
+
+ io.quarkus
+ quarkus-config-yaml
+
+
+
+
+
+
+ io.quarkus.platform
+ quarkus-maven-plugin
+
+
+
+
+
+
+ database-migrate
+
+
+
+ org.liquibase
+ liquibase-maven-plugin
+ 4.26.0
+
+ db/changeLog.xml
+ jdbc:${env.DB_URL}
+ ${env.DB_USERNAME}
+ ${env.DB_PASSWORD}
+
+
+
+
+
+
+
+
+
+ uPagge
+ Struchkov Mark
+ mark@struchkov.dev
+ https://mark.struchkov.dev
+
+
+
+
\ No newline at end of file
diff --git a/network-backend/src/main/java/dev/struchkov/network/config/LiquibaseConfig.java b/network-backend/src/main/java/dev/struchkov/network/config/LiquibaseConfig.java
new file mode 100644
index 0000000..1df0c3d
--- /dev/null
+++ b/network-backend/src/main/java/dev/struchkov/network/config/LiquibaseConfig.java
@@ -0,0 +1,59 @@
+package dev.struchkov.network.config;
+
+import io.quarkus.runtime.StartupEvent;
+import jakarta.enterprise.event.Observes;
+import jakarta.inject.Singleton;
+import liquibase.Contexts;
+import liquibase.LabelExpression;
+import liquibase.Liquibase;
+import liquibase.database.DatabaseFactory;
+import liquibase.exception.LiquibaseException;
+import liquibase.resource.ClassLoaderResourceAccessor;
+import liquibase.resource.ResourceAccessor;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class LiquibaseConfig {
+
+ private static final Logger log = LoggerFactory.getLogger(LiquibaseConfig.class);
+
+ @ConfigProperty(name = "quarkus.datasource.reactive.url")
+ String datasourceUrl;
+
+ @ConfigProperty(name = "quarkus.datasource.username")
+ String datasourceUsername;
+
+ @ConfigProperty(name = "quarkus.datasource.password")
+ String datasourcePassword;
+
+ @ConfigProperty(name = "quarkus.liquibase.change-log")
+ String changeLogLocation;
+
+ @ConfigProperty(name = "quarkus.liquibase.clean-at-start", defaultValue = "false")
+ boolean cleanAtStart;
+
+ public void runLiquibaseMigration(@Observes StartupEvent event) {
+ System.setProperty("liquibase.secureParsing", "false");
+ final ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(Thread.currentThread().getContextClassLoader());
+ try (final Liquibase liquibase = new Liquibase(
+ changeLogLocation,
+ resourceAccessor,
+ DatabaseFactory.getInstance()
+ .openConnection("jdbc:" + datasourceUrl, datasourceUsername, datasourcePassword, null, resourceAccessor))
+ ) {
+ if (cleanAtStart) {
+ log.warn("Liquibase drop database");
+ liquibase.dropAll();
+ }
+ liquibase.update(new Contexts(), new LabelExpression());
+ } catch (LiquibaseException e) {
+ log.error("Liquibase error: {}", e.getMessage());
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ }
+ System.setProperty("liquibase.secureParsing", "true");
+ }
+
+}
diff --git a/src/main/resources/META-INF/resources/index.html b/network-backend/src/main/resources/META-INF/resources/index.html
similarity index 100%
rename from src/main/resources/META-INF/resources/index.html
rename to network-backend/src/main/resources/META-INF/resources/index.html
diff --git a/network-backend/src/main/resources/application.yml b/network-backend/src/main/resources/application.yml
new file mode 100644
index 0000000..2b1e515
--- /dev/null
+++ b/network-backend/src/main/resources/application.yml
@@ -0,0 +1,42 @@
+quarkus:
+ application:
+ name: MySpace
+ banner:
+ path: banner.txt
+ http:
+ port: ${PORT:8080}
+ root-path: /myspace
+ smallrye-openapi:
+ security-scheme: jwt
+ datasource:
+ jdbc: false
+ db-kind: postgresql
+ username: ${DB_USERNAME}
+ password: ${DB_PASSWORD}
+ reactive:
+ url: ${DB_URL}
+ max-size: ${DB_POOL_SIZE:100}
+ hibernate-orm:
+ database:
+ default-schema: public
+ generation: none
+ native:
+ resources:
+ includes:
+ - ssl/publicKey.pem
+smallrye:
+ jwt:
+ sign:
+ key:
+ location: ssl/privateKey.pem
+mp:
+ jwt:
+ verify:
+ publickey:
+ location: ssl/publicKey.pem
+ issuer: http://localhost:8080
+"%local-dev":
+ quarkus:
+ hibernate-orm:
+ log:
+ sql: true
\ No newline at end of file
diff --git a/network-backend/src/main/resources/banner.txt b/network-backend/src/main/resources/banner.txt
new file mode 100644
index 0000000..d0c9ab4
--- /dev/null
+++ b/network-backend/src/main/resources/banner.txt
@@ -0,0 +1,8 @@
+
+░▒▓██████████████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓███████▓▒░▒▓███████▓▒░ ░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓████████▓▒░
+░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
+░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░
+░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░░▒▓████████▓▒░▒▓█▓▒░ ░▒▓██████▓▒░
+░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░
+░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
+░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓███████▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓████████▓▒░
diff --git a/network-backend/src/main/resources/db/changeLog.xml b/network-backend/src/main/resources/db/changeLog.xml
new file mode 100644
index 0000000..82a49fa
--- /dev/null
+++ b/network-backend/src/main/resources/db/changeLog.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/network-backend/src/main/resources/db/v.1.0.0/2024-03-02-create-person-table.xml b/network-backend/src/main/resources/db/v.1.0.0/2024-03-02-create-person-table.xml
new file mode 100644
index 0000000..a6b0b15
--- /dev/null
+++ b/network-backend/src/main/resources/db/v.1.0.0/2024-03-02-create-person-table.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/network-backend/src/main/resources/db/v.1.0.0/changelog.xml b/network-backend/src/main/resources/db/v.1.0.0/changelog.xml
new file mode 100644
index 0000000..26bbe72
--- /dev/null
+++ b/network-backend/src/main/resources/db/v.1.0.0/changelog.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/network-backend/src/main/resources/ssl/privateKey.pem b/network-backend/src/main/resources/ssl/privateKey.pem
new file mode 100644
index 0000000..9628fba
--- /dev/null
+++ b/network-backend/src/main/resources/ssl/privateKey.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDl4OXoNoq5C3mQ
+hpP+7gwuQEl0jnfuBt2mCWpnHKVEZcksneGXDwvIM2BH/vQBG8PL3DtlhJRwcCpY
+dY3JOascdNBzGGQIqKOi142TAUI8yjGJ8PxtiB1Wkdhvuyb/vshaIcEZCZPondTA
+yl+h5TnOYdA7abC+RDB7d6tnLl9YcAlR/Qoq6Tflrq0HtQhXb2aE2mjbd+uI8RFp
+nzeD4v89cghvQMwUv/cVHsNiILek7++s4lTSe+ScLqqHAwCgVxO8hOjOybOEEb3+
+LJpKu8E8BanM7Smujd5LFwvB6TBejD/i/Yreaqi9vLaNDRGjDn0CK2njcHkEPu9g
+5ynIj/dHAgMBAAECggEAGLOoYOHuLuDnHjr9Oj6q5mltZe336zGnrbODUIiJJdFo
+4wzQH020RBNpPDNPAF4oawaZB1lOI3WaHJ6n2WtYp2/SBRwmMQarbCXiMQgd7a8D
+JcHUjjyQfStM6QILuHLwJTyipaXG1A8/ERHDYaqNIfqWyAZHwhpND2drmWR9RbZd
+b/47AXctgCB48Eb1SGG20Q0l//vdpqpO/Kowchs2XPuLIlmpe/5NtAkNtfTIXciA
+clBxz6r8Z/6HLA4paAFpvBy+RzfPqhDLRYIi09UCxWVSvHmXgdSJV6mHJ45K5jv6
+hNs3dVm7sjuXKjQLhVdZPjavXPTspci9wlE6CGa7AQKBgQD/f04OQuCSCe67wUKC
+fjICE/paK0Mr6/qc5Be/d24XC2wPj0xytxtD7IDjsgGklGtAYBqi8hg+yp1d7rOx
+twqSWLyJL2tpkYpSiFYZuRRtwhT6YPNhUwmu88RMFbuvSw/N1YAd0Nc2o+QjH2C4
+tv/4Szbl3Aoovmr/gEJS+tKGgQKBgQDmVLBacZTeiy77ag0qmAA95HyV4ANldDnm
+ZmTJFv+6kSvC7tRm7ONxDAqDf/+Ip0kGAVFwRRpGgWcQkcQ478/2U2tS0OmLQF2E
+s5i3b9DwV8dDjLqLLK6KVYKnl7hEEjTE52pLKjOxpQ/prIqvBmRiRO9YT5uSJwYh
+hQCHf37pxwKBgQDPYrotAUPfxogthrVBzsUwwp6Xyj3/zM/jwZE1LPADbJYOGzbm
+dpJ/E92a9MY4nPBhlKCKWxArB7OspzDcs8K0/6opgIAjdKteSqP4xS472SnGAiQL
+f6eAhwVy7MEnjDoLzZzslrPZ+jYX/EQ6KvCizgqFkZH9eHLJaYZsbIOuAQKBgQDe
+/EhLF0DvfkG6xr3+aW96e/SdSGUhWDchtimGgIyNOzPpoTvidR3v+J9JFab2zjUk
+6ivEjDHW5jo4UVZtZMWpjLMPVw9yb5rZ+OE1XbwiomhYHisOx/AmHojB5WFQDWwm
+4H8IS9AGAG36ZL9SmNagSGqKzW8oPD10QAX2J9lfSwKBgFGNa8cbfVuaMqntYYdE
+cMaBr3zD2sjARN8nYcK2/a/o4nK6zcXaMHSuDRsYKwZsD+xjoqiI8kBHfGxYiF4Y
+f+F5y/UJDngH8vHIpNkJKxaSfC28D/SiSKMQGFjbXn4pZ6eAnqYPsO2PWiglyQpH
+yFrkfB0T2zdoYGVQ0kowSYiU
+-----END PRIVATE KEY-----
diff --git a/network-backend/src/main/resources/ssl/publicKey.pem b/network-backend/src/main/resources/ssl/publicKey.pem
new file mode 100644
index 0000000..881e22d
--- /dev/null
+++ b/network-backend/src/main/resources/ssl/publicKey.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5eDl6DaKuQt5kIaT/u4M
+LkBJdI537gbdpglqZxylRGXJLJ3hlw8LyDNgR/70ARvDy9w7ZYSUcHAqWHWNyTmr
+HHTQcxhkCKijoteNkwFCPMoxifD8bYgdVpHYb7sm/77IWiHBGQmT6J3UwMpfoeU5
+zmHQO2mwvkQwe3erZy5fWHAJUf0KKuk35a6tB7UIV29mhNpo23friPERaZ83g+L/
+PXIIb0DMFL/3FR7DYiC3pO/vrOJU0nvknC6qhwMAoFcTvITozsmzhBG9/iyaSrvB
+PAWpzO0pro3eSxcLwekwXow/4v2K3mqovby2jQ0Row59Aitp43B5BD7vYOcpyI/3
+RwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/network-context/pom.xml b/network-context/pom.xml
new file mode 100644
index 0000000..0757197
--- /dev/null
+++ b/network-context/pom.xml
@@ -0,0 +1,35 @@
+
+
+ 4.0.0
+
+ dev.struchkov.network
+ social-network
+ 1.0.0-SNAPSHOT
+
+
+ network-context
+
+
+
+ dev.struchkov.network
+ network-domain
+
+
+
+ io.smallrye.reactive
+ mutiny
+
+
+
+
+
+ uPagge
+ Struchkov Mark
+ mark@struchkov.dev
+ https://mark.struchkov.dev
+
+
+
+
\ No newline at end of file
diff --git a/network-context/src/main/java/dev/struchkov/network/context/repository/PersonRepository.java b/network-context/src/main/java/dev/struchkov/network/context/repository/PersonRepository.java
new file mode 100644
index 0000000..82ab89a
--- /dev/null
+++ b/network-context/src/main/java/dev/struchkov/network/context/repository/PersonRepository.java
@@ -0,0 +1,16 @@
+package dev.struchkov.network.context.repository;
+
+import dev.struchkov.network.domain.entity.Person;
+import io.smallrye.mutiny.Uni;
+
+import java.util.UUID;
+
+public interface PersonRepository {
+
+ Uni save(Person person);
+
+ Uni findById(UUID personId);
+
+ Uni findByEmail(String email);
+
+}
diff --git a/network-context/src/main/java/dev/struchkov/network/context/service/AuthService.java b/network-context/src/main/java/dev/struchkov/network/context/service/AuthService.java
new file mode 100644
index 0000000..2070387
--- /dev/null
+++ b/network-context/src/main/java/dev/struchkov/network/context/service/AuthService.java
@@ -0,0 +1,9 @@
+package dev.struchkov.network.context.service;
+
+import io.smallrye.mutiny.Uni;
+
+public interface AuthService {
+
+ Uni login(String email, String password);
+
+}
diff --git a/network-context/src/main/java/dev/struchkov/network/context/service/PersonService.java b/network-context/src/main/java/dev/struchkov/network/context/service/PersonService.java
new file mode 100644
index 0000000..fca30c4
--- /dev/null
+++ b/network-context/src/main/java/dev/struchkov/network/context/service/PersonService.java
@@ -0,0 +1,20 @@
+package dev.struchkov.network.context.service;
+
+import dev.struchkov.network.domain.entity.Person;
+import dev.struchkov.network.domain.person.PersonCreate;
+import io.smallrye.mutiny.Uni;
+import jakarta.validation.Valid;
+
+import java.util.UUID;
+
+public interface PersonService {
+
+ Uni register(@Valid PersonCreate createData);
+
+ Uni getById(UUID userId);
+
+ Uni getByIdOrThrown(UUID userId);
+
+ Uni getByEmail(String email);
+
+}
diff --git a/network-controller-rest/pom.xml b/network-controller-rest/pom.xml
new file mode 100644
index 0000000..9f9ba70
--- /dev/null
+++ b/network-controller-rest/pom.xml
@@ -0,0 +1,64 @@
+
+
+ 4.0.0
+
+ dev.struchkov.network
+ social-network
+ 1.0.0-SNAPSHOT
+
+
+ network-controller-rest
+
+
+
+ dev.struchkov.otus.network
+ network-exception
+
+
+ dev.struchkov.network
+ network-context
+
+
+ dev.struchkov.network
+ network-schema-rest
+
+
+
+ io.quarkus
+ quarkus-resteasy-reactive-jackson
+
+
+ io.quarkus
+ quarkus-resteasy-reactive
+
+
+ io.quarkus
+ quarkus-smallrye-openapi
+
+
+ io.quarkus
+ quarkus-smallrye-jwt
+
+
+
+ org.projectlombok
+ lombok
+
+
+ org.mapstruct
+ mapstruct
+
+
+
+
+
+ uPagge
+ Struchkov Mark
+ mark@struchkov.dev
+ https://mark.struchkov.dev
+
+
+
+
\ No newline at end of file
diff --git a/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/PersonRestController.java b/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/PersonRestController.java
new file mode 100644
index 0000000..bc9d360
--- /dev/null
+++ b/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/PersonRestController.java
@@ -0,0 +1,74 @@
+package dev.struchkov.network.controller.rest;
+
+import dev.struchkov.network.context.service.AuthService;
+import dev.struchkov.network.context.service.PersonService;
+import dev.struchkov.network.controller.rest.convert.PersonMapper;
+import dev.struchkov.network.controller.rest.schema.input.PersonLoginRequest;
+import dev.struchkov.network.controller.rest.schema.input.PersonRegisterRequest;
+import dev.struchkov.network.controller.rest.schema.output.PersonLoginResponse;
+import dev.struchkov.network.controller.rest.schema.output.PersonResponse;
+import io.smallrye.mutiny.Uni;
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.security.PermitAll;
+import jakarta.annotation.security.RolesAllowed;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import lombok.RequiredArgsConstructor;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
+import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
+import org.eclipse.microprofile.openapi.annotations.tags.Tag;
+
+import java.util.UUID;
+
+@Path("user")
+@RequiredArgsConstructor
+@Tag(name = "Пользователи", description = "Управление пользователями системы.")
+public class PersonRestController {
+
+ private final PersonService personService;
+ private final AuthService authService;
+
+ private final PersonMapper personMapper;
+
+ @POST
+ @PermitAll
+ @Path("register")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Регистрация пользователя")
+ public Uni register(@RequestBody PersonRegisterRequest input) {
+ return personService.register(personMapper.toDomain(input))
+ .map(personMapper::toOutput);
+ }
+
+ @POST
+ @PermitAll
+ @Path("login")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Авторизация пользователей")
+ public Uni login(@RequestBody PersonLoginRequest input) {
+ return authService.login(input.getEmail(), input.getPassword())
+ .map(accessToken -> PersonLoginResponse.builder().accessToken(accessToken).build());
+ }
+
+ @GET
+ @Path("{userId}")
+ @RolesAllowed({"USER"})
+ @Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Получить пользователя по идентификатору пользователей")
+ public Uni getById(
+ @Parameter(required = true, description = "Идентификатор пользователя") @PathParam("userId") @Nonnull UUID userId
+ ) {
+ return personService.getByIdOrThrown(userId)
+ .map(personMapper::toOutput);
+ }
+
+
+}
diff --git a/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/convert/PersonMapper.java b/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/convert/PersonMapper.java
new file mode 100644
index 0000000..9d564ca
--- /dev/null
+++ b/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/convert/PersonMapper.java
@@ -0,0 +1,18 @@
+package dev.struchkov.network.controller.rest.convert;
+
+import dev.struchkov.network.controller.rest.schema.input.PersonRegisterRequest;
+import dev.struchkov.network.controller.rest.schema.output.PersonResponse;
+import dev.struchkov.network.domain.entity.Person;
+import dev.struchkov.network.domain.person.PersonCreate;
+import org.mapstruct.Mapper;
+
+import static org.mapstruct.MappingConstants.ComponentModel.JAKARTA;
+
+@Mapper(componentModel = JAKARTA)
+public interface PersonMapper {
+
+ PersonResponse toOutput(Person person);
+
+ PersonCreate toDomain(PersonRegisterRequest input);
+
+}
diff --git a/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/handler/ConstraintViolationExceptionMapper.java b/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/handler/ConstraintViolationExceptionMapper.java
new file mode 100644
index 0000000..107cacd
--- /dev/null
+++ b/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/handler/ConstraintViolationExceptionMapper.java
@@ -0,0 +1,29 @@
+package dev.struchkov.network.controller.rest.handler;
+
+import dev.struchkov.network.controller.rest.schema.error.ValidationErrorData;
+import dev.struchkov.network.controller.rest.schema.error.ValidationErrorMessage;
+import jakarta.validation.ConstraintViolationException;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ExceptionMapper;
+import jakarta.ws.rs.ext.Provider;
+
+@Provider
+public class ConstraintViolationExceptionMapper implements ExceptionMapper {
+
+ @Override
+ public Response toResponse(ConstraintViolationException e) {
+ return Response.status(Response.Status.BAD_REQUEST)
+ .entity(
+ ValidationErrorMessage.builder()
+ .errors(
+ e.getConstraintViolations().stream()
+ .map(constraintViolation -> ValidationErrorData.builder().message(constraintViolation.getMessage()).build())
+ .toList()
+ )
+ .build()
+
+ )
+ .build();
+ }
+
+}
diff --git a/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/handler/ExceptionHandler.java b/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/handler/ExceptionHandler.java
new file mode 100644
index 0000000..beda4a8
--- /dev/null
+++ b/network-controller-rest/src/main/java/dev/struchkov/network/controller/rest/handler/ExceptionHandler.java
@@ -0,0 +1,27 @@
+package dev.struchkov.network.controller.rest.handler;
+
+import dev.struchkov.network.controller.rest.schema.error.ErrorMessage;
+import dev.struchkov.network.exception.NotFoundException;
+import dev.struchkov.network.exception.SocialNetworkException;
+import jakarta.ws.rs.core.Response;
+import org.jboss.resteasy.reactive.RestResponse;
+import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
+
+public class ExceptionHandler {
+
+ @ServerExceptionMapper
+ public RestResponse mapException(RuntimeException e) {
+ return RestResponse.status(Response.Status.BAD_REQUEST, ErrorMessage.builder().message(e.getMessage()).build());
+ }
+
+ @ServerExceptionMapper
+ public RestResponse mapException(SocialNetworkException e) {
+ return RestResponse.status(Response.Status.BAD_REQUEST, ErrorMessage.builder().message(e.getMessage()).build());
+ }
+
+ @ServerExceptionMapper
+ public RestResponse mapException(NotFoundException e) {
+ return RestResponse.status(Response.Status.NOT_FOUND, ErrorMessage.builder().message(e.getMessage()).build());
+ }
+
+}
diff --git a/network-core/pom.xml b/network-core/pom.xml
new file mode 100644
index 0000000..8586a2d
--- /dev/null
+++ b/network-core/pom.xml
@@ -0,0 +1,60 @@
+
+
+ 4.0.0
+
+ dev.struchkov.network
+ social-network
+ 1.0.0-SNAPSHOT
+
+
+ network-core
+
+
+
+ dev.struchkov.otus.network
+ network-exception
+
+
+ dev.struchkov.network
+ network-context
+
+
+
+ io.quarkus
+ quarkus-hibernate-validator
+
+
+ io.quarkus
+ quarkus-hibernate-reactive-panache-common
+
+
+ io.quarkus
+ quarkus-smallrye-jwt-build
+
+
+
+ org.projectlombok
+ lombok
+
+
+ com.github.f4b6a3
+ uuid-creator
+
+
+ org.mindrot
+ jbcrypt
+
+
+
+
+
+ uPagge
+ Struchkov Mark
+ mark@struchkov.dev
+ https://mark.struchkov.dev
+
+
+
+
\ No newline at end of file
diff --git a/network-core/src/main/java/dev/struchkov/network/core/service/AuthServiceImpl.java b/network-core/src/main/java/dev/struchkov/network/core/service/AuthServiceImpl.java
new file mode 100644
index 0000000..db68924
--- /dev/null
+++ b/network-core/src/main/java/dev/struchkov/network/core/service/AuthServiceImpl.java
@@ -0,0 +1,43 @@
+package dev.struchkov.network.core.service;
+
+import dev.struchkov.network.context.service.AuthService;
+import dev.struchkov.network.context.service.PersonService;
+import dev.struchkov.network.exception.AuthException;
+import io.smallrye.jwt.build.Jwt;
+import io.smallrye.mutiny.Uni;
+import jakarta.enterprise.context.ApplicationScoped;
+import lombok.RequiredArgsConstructor;
+
+import java.time.Duration;
+import java.util.Collections;
+
+import static dev.struchkov.network.core.util.PasswordUtils.checkPassword;
+import static dev.struchkov.network.exception.AuthException.authException;
+
+@ApplicationScoped
+@RequiredArgsConstructor
+public class AuthServiceImpl implements AuthService {
+
+ private final PersonService personService;
+
+ @Override
+ public Uni login(String email, String password) {
+ return personService.getByEmail(email)
+ .onItem().ifNull().failWith(authException("Ошибка авторизации"))
+ .invoke(person -> {
+ if (!checkPassword(password, person.getHashPassword())) {
+ throw new AuthException("Ошибка авторизации");
+ }
+ })
+ .map(
+ person ->
+ Jwt.issuer("http://localhost:8080")
+ .upn(person.getId().toString())
+ .expiresIn(Duration.ofMinutes(15))
+ .groups(Collections.singleton("USER"))
+ .claim("email", person.getEmail())
+ .sign()
+ );
+ }
+
+}
diff --git a/network-core/src/main/java/dev/struchkov/network/core/service/PersonServiceImpl.java b/network-core/src/main/java/dev/struchkov/network/core/service/PersonServiceImpl.java
new file mode 100644
index 0000000..ec7ac68
--- /dev/null
+++ b/network-core/src/main/java/dev/struchkov/network/core/service/PersonServiceImpl.java
@@ -0,0 +1,68 @@
+package dev.struchkov.network.core.service;
+
+import com.github.f4b6a3.uuid.UuidCreator;
+import dev.struchkov.network.context.repository.PersonRepository;
+import dev.struchkov.network.context.service.PersonService;
+import dev.struchkov.network.domain.entity.Person;
+import dev.struchkov.network.domain.person.PersonCreate;
+import dev.struchkov.network.exception.NotFoundException;
+import io.quarkus.hibernate.reactive.panache.common.WithSession;
+import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
+import io.smallrye.mutiny.Uni;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+
+import java.util.UUID;
+
+import static dev.struchkov.network.core.util.PasswordUtils.hashPassword;
+import static dev.struchkov.network.exception.RegistrationException.registrationException;
+
+@ApplicationScoped
+@RequiredArgsConstructor
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonRepository repository;
+
+ @Override
+ @WithTransaction
+ public Uni register(@Valid PersonCreate createData) {
+ return repository.findByEmail(createData.getEmail())
+ .onItem().ifNotNull().failWith(registrationException("Пользователь с таким email уже зарегистрирован."))
+ .onItem().ifNull().switchTo(
+ () -> repository.save(
+ Person.builder()
+ .id(UuidCreator.getTimeOrderedEpoch())
+ .email(createData.getEmail())
+ .firstName(createData.getFirstName())
+ .surname(createData.getSurname())
+ .biography(createData.getBiography())
+ .hashPassword(hashPassword(createData.getPassword()))
+ .birthday(createData.getBirthday())
+ .city(createData.getCity())
+ .build()
+ )
+ );
+
+ }
+
+ @Override
+ @WithSession
+ public Uni getById(UUID userId) {
+ return repository.findById(userId);
+ }
+
+ @Override
+ @WithSession
+ public Uni getByIdOrThrown(UUID userId) {
+ return repository.findById(userId)
+ .onItem().ifNull().failWith(NotFoundException.notFoundException("Пользователь не найден"));
+ }
+
+ @Override
+ @WithSession
+ public Uni getByEmail(String email) {
+ return repository.findByEmail(email);
+ }
+
+}
diff --git a/network-core/src/main/java/dev/struchkov/network/core/util/PasswordUtils.java b/network-core/src/main/java/dev/struchkov/network/core/util/PasswordUtils.java
new file mode 100644
index 0000000..ca10f95
--- /dev/null
+++ b/network-core/src/main/java/dev/struchkov/network/core/util/PasswordUtils.java
@@ -0,0 +1,17 @@
+package dev.struchkov.network.core.util;
+
+import lombok.experimental.UtilityClass;
+import org.mindrot.jbcrypt.BCrypt;
+
+@UtilityClass
+public class PasswordUtils {
+
+ public static String hashPassword(String password) {
+ return BCrypt.hashpw(password, BCrypt.gensalt());
+ }
+
+ public static boolean checkPassword(String candidate, String hashedPassword) {
+ return BCrypt.checkpw(candidate, hashedPassword);
+ }
+
+}
diff --git a/network-data-jooq/pom.xml b/network-data-jooq/pom.xml
new file mode 100644
index 0000000..db30bdd
--- /dev/null
+++ b/network-data-jooq/pom.xml
@@ -0,0 +1,94 @@
+
+
+ 4.0.0
+
+ dev.struchkov.network
+ social-network
+ 1.0.0-SNAPSHOT
+
+
+ network-data-jooq
+
+
+
+ dev.struchkov.network
+ network-context
+
+
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
+
+
+ io.smallrye.reactive
+ smallrye-mutiny-vertx-pg-client
+
+
+ io.quarkus
+ quarkus-reactive-pg-client
+
+
+
+
+ org.jooq
+ jooq
+ 3.19.5
+
+
+ org.postgresql
+ postgresql
+
+
+ org.projectlombok
+ lombok
+
+
+ org.mapstruct
+ mapstruct
+
+
+
+
+
+ jooq-generate
+
+
+
+ org.jooq
+ jooq-codegen-maven
+ 3.19.5
+
+
+
+ generate
+
+
+
+
+
+ org.postgresql.Driver
+ jdbc:${env.DB_URL}
+ ${env.DB_USERNAME}
+ ${env.DB_PASSWORD}
+
+
+
+ org.jooq.meta.postgres.PostgresDatabase
+ .*
+
+
+ dev.struchkov.network.data.jooq.schema
+ src/main/java
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/PersonRepositoryImpl.java b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/PersonRepositoryImpl.java
new file mode 100644
index 0000000..a60e8c8
--- /dev/null
+++ b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/PersonRepositoryImpl.java
@@ -0,0 +1,82 @@
+package dev.struchkov.network.data.jooq;
+
+import dev.struchkov.network.context.repository.PersonRepository;
+import dev.struchkov.network.data.jooq.mapper.PersonDataMapper;
+import dev.struchkov.network.domain.entity.Person;
+import dev.struchkov.network.domain.entity.PersonColumns;
+import io.smallrye.mutiny.Uni;
+import io.vertx.mutiny.pgclient.PgPool;
+import io.vertx.mutiny.sqlclient.PreparedQuery;
+import io.vertx.mutiny.sqlclient.Query;
+import io.vertx.mutiny.sqlclient.Row;
+import io.vertx.mutiny.sqlclient.RowSet;
+import jakarta.enterprise.context.ApplicationScoped;
+import lombok.RequiredArgsConstructor;
+import org.jooq.SQLDialect;
+import org.jooq.conf.ParamType;
+import org.jooq.impl.DefaultDSLContext;
+
+import java.util.UUID;
+import java.util.function.Function;
+
+import static dev.struchkov.network.data.jooq.schema.public_.tables.Person.PERSON;
+
+@ApplicationScoped
+@RequiredArgsConstructor
+public class PersonRepositoryImpl implements PersonRepository {
+
+ private static final DefaultDSLContext dsl = new DefaultDSLContext(SQLDialect.POSTGRES);
+
+ private final PgPool pgPool;
+ private final PersonDataMapper mapper;
+
+ @Override
+ public Uni save(Person person) {
+ return Uni.createFrom().item(dsl.insertInto(PERSON).set(mapper.toRecord(person)).getSQL(ParamType.NAMED_OR_INLINED))
+ .map(pgPool::query)
+ .flatMap(Query::execute)
+ .onItem().transformToMulti(RowSet::toMulti)
+ .collect().last()
+ .onItem().ifNotNull().transform(toDomain());
+ }
+
+ @Override
+ public Uni findById(UUID personId) {
+ return Uni.createFrom().item(
+ dsl.selectFrom(PERSON)
+ .where(PERSON.ID.eq(personId))
+ .getSQL(ParamType.NAMED_OR_INLINED)
+ ).map(pgPool::preparedQuery)
+ .flatMap(PreparedQuery::execute)
+ .onItem().transformToMulti(RowSet::toMulti)
+ .collect().last()
+ .onItem().ifNotNull().transform(toDomain());
+ }
+
+ @Override
+ public Uni findByEmail(String email) {
+ return Uni.createFrom().item(
+ dsl.selectFrom(PERSON)
+ .where(PERSON.EMAIL.eq(email))
+ .getSQL(ParamType.NAMED_OR_INLINED)
+ ).map(pgPool::preparedQuery)
+ .flatMap(PreparedQuery::execute)
+ .onItem().transformToMulti(RowSet::toMulti)
+ .collect().last()
+ .onItem().ifNotNull().transform(toDomain());
+ }
+
+ private static Function toDomain() {
+ return row -> Person.builder()
+ .id(row.getUUID(PersonColumns.id))
+ .city(row.getString(PersonColumns.city))
+ .surname(row.getString(PersonColumns.surname))
+ .hashPassword(row.getString(PersonColumns.hashPassword))
+ .firstName(row.getString(PersonColumns.firstName))
+ .birthday(row.getLocalDate(PersonColumns.birthday))
+ .biography(row.getString(PersonColumns.biography))
+ .email(row.getString(PersonColumns.email))
+ .build();
+ }
+
+}
diff --git a/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/mapper/PersonDataMapper.java b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/mapper/PersonDataMapper.java
new file mode 100644
index 0000000..00efebb
--- /dev/null
+++ b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/mapper/PersonDataMapper.java
@@ -0,0 +1,16 @@
+package dev.struchkov.network.data.jooq.mapper;
+
+import dev.struchkov.network.data.jooq.schema.public_.tables.records.PersonRecord;
+import dev.struchkov.network.domain.entity.Person;
+import org.mapstruct.Mapper;
+
+import static org.mapstruct.MappingConstants.ComponentModel.JAKARTA;
+
+@Mapper(componentModel = JAKARTA)
+public interface PersonDataMapper {
+
+ Person toDomain(PersonRecord record);
+
+ PersonRecord toRecord(Person domain);
+
+}
diff --git a/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/schema/DefaultCatalog.java b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/schema/DefaultCatalog.java
new file mode 100644
index 0000000..aad59d6
--- /dev/null
+++ b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/schema/DefaultCatalog.java
@@ -0,0 +1,69 @@
+/*
+ * This file is generated by jOOQ.
+ */
+package dev.struchkov.network.data.jooq.schema;
+
+
+import dev.struchkov.network.data.jooq.schema.information_schema.InformationSchema;
+import dev.struchkov.network.data.jooq.schema.pg_catalog.PgCatalog;
+import dev.struchkov.network.data.jooq.schema.public_.Public;
+import org.jooq.Constants;
+import org.jooq.Schema;
+import org.jooq.impl.CatalogImpl;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * This class is generated by jOOQ.
+ */
+@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" })
+public class DefaultCatalog extends CatalogImpl {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The reference instance of DEFAULT_CATALOG
+ */
+ public static final DefaultCatalog DEFAULT_CATALOG = new DefaultCatalog();
+
+ /**
+ * The schema information_schema.
+ */
+ public final InformationSchema INFORMATION_SCHEMA = InformationSchema.INFORMATION_SCHEMA;
+
+ /**
+ * The schema pg_catalog.
+ */
+ public final PgCatalog PG_CATALOG = PgCatalog.PG_CATALOG;
+
+ /**
+ * The schema public.
+ */
+ public final Public PUBLIC = Public.PUBLIC;
+
+ /**
+ * No further instances allowed
+ */
+ private DefaultCatalog() {
+ super("");
+ }
+
+ @Override
+ public final List getSchemas() {
+ return Arrays.asList(
+ InformationSchema.INFORMATION_SCHEMA,
+ PgCatalog.PG_CATALOG,
+ Public.PUBLIC
+ );
+ }
+
+ /**
+ * A reference to the 3.19 minor release of the code generator. If this
+ * doesn't compile, it's because the runtime library uses an older minor
+ * release, namely: 3.19. You can turn off the generation of this reference
+ * by specifying /configuration/generator/generate/jooqVersionReference
+ */
+ private static final String REQUIRE_RUNTIME_JOOQ_VERSION = Constants.VERSION_3_19;
+}
diff --git a/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/schema/information_schema/Domains.java b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/schema/information_schema/Domains.java
new file mode 100644
index 0000000..94d2d33
--- /dev/null
+++ b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/schema/information_schema/Domains.java
@@ -0,0 +1,73 @@
+/*
+ * This file is generated by jOOQ.
+ */
+package dev.struchkov.network.data.jooq.schema.information_schema;
+
+
+import org.jooq.Domain;
+import org.jooq.Schema;
+import org.jooq.impl.DSL;
+import org.jooq.impl.Internal;
+import org.jooq.impl.LazySchema;
+import org.jooq.impl.SQLDataType;
+
+import java.time.OffsetDateTime;
+
+
+/**
+ * Convenience access to all Domains in information_schema.
+ */
+@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" })
+public class Domains {
+
+ /**
+ * The domain information_schema.cardinal_number.
+ */
+ public static final Domain CARDINAL_NUMBER = Internal.createDomain(
+ schema()
+ , DSL.name("cardinal_number")
+ , SQLDataType.INTEGER
+ , Internal.createCheck(null, null, "CHECK ((VALUE >= 0))")
+ );
+
+ /**
+ * The domain information_schema.character_data.
+ */
+ public static final Domain CHARACTER_DATA = Internal.createDomain(
+ schema()
+ , DSL.name("character_data")
+ , SQLDataType.VARCHAR
+ );
+
+ /**
+ * The domain information_schema.sql_identifier.
+ */
+ public static final Domain SQL_IDENTIFIER = Internal.createDomain(
+ schema()
+ , DSL.name("sql_identifier")
+ , SQLDataType.VARCHAR
+ );
+
+ /**
+ * The domain information_schema.time_stamp.
+ */
+ public static final Domain TIME_STAMP = Internal.createDomain(
+ schema()
+ , DSL.name("time_stamp")
+ , SQLDataType.TIMESTAMPWITHTIMEZONE(2).defaultValue(DSL.field(DSL.raw("CURRENT_TIMESTAMP(2)"), SQLDataType.TIMESTAMPWITHTIMEZONE))
+ );
+
+ /**
+ * The domain information_schema.yes_or_no.
+ */
+ public static final Domain YES_OR_NO = Internal.createDomain(
+ schema()
+ , DSL.name("yes_or_no")
+ , SQLDataType.VARCHAR(3)
+ , Internal.createCheck(null, null, "CHECK (((VALUE)::text = ANY ((ARRAY['YES'::character varying, 'NO'::character varying])::text[])))")
+ );
+
+ private static final Schema schema() {
+ return new LazySchema(DSL.name("information_schema"), DSL.comment(""), () -> InformationSchema.INFORMATION_SCHEMA);
+ }
+}
diff --git a/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/schema/information_schema/InformationSchema.java b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/schema/information_schema/InformationSchema.java
new file mode 100644
index 0000000..8850eaf
--- /dev/null
+++ b/network-data-jooq/src/main/java/dev/struchkov/network/data/jooq/schema/information_schema/InformationSchema.java
@@ -0,0 +1,590 @@
+/*
+ * This file is generated by jOOQ.
+ */
+package dev.struchkov.network.data.jooq.schema.information_schema;
+
+
+import dev.struchkov.network.data.jooq.schema.DefaultCatalog;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.AdministrableRoleAuthorizations;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ApplicableRoles;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Attributes;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.CharacterSets;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.CheckConstraintRoutineUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.CheckConstraints;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.CollationCharacterSetApplicability;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Collations;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ColumnColumnUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ColumnDomainUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ColumnOptions;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ColumnPrivileges;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ColumnUdtUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Columns;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ConstraintColumnUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ConstraintTableUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.DataTypePrivileges;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.DomainConstraints;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.DomainUdtUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Domains;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ElementTypes;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.EnabledRoles;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ForeignDataWrapperOptions;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ForeignDataWrappers;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ForeignServerOptions;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ForeignServers;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ForeignTableOptions;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ForeignTables;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.InformationSchemaCatalogName;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.KeyColumnUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Parameters;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ReferentialConstraints;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoleColumnGrants;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoleRoutineGrants;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoleTableGrants;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoleUdtGrants;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoleUsageGrants;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoutineColumnUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoutinePrivileges;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoutineRoutineUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoutineSequenceUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.RoutineTableUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Routines;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Schemata;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Sequences;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.SqlFeatures;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.SqlImplementationInfo;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.SqlParts;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.SqlSizing;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.TableConstraints;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.TablePrivileges;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Tables;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Transforms;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.TriggeredUpdateColumns;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Triggers;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.UdtPrivileges;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.UsagePrivileges;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.UserDefinedTypes;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.UserMappingOptions;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.UserMappings;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ViewColumnUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ViewRoutineUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.ViewTableUsage;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.Views;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables._PgExpandarray;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables._PgForeignDataWrappers;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables._PgForeignServers;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables._PgForeignTableColumns;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables._PgForeignTables;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables._PgUserMappings;
+import dev.struchkov.network.data.jooq.schema.information_schema.tables.records._PgExpandarrayRecord;
+import org.jooq.Catalog;
+import org.jooq.Configuration;
+import org.jooq.Domain;
+import org.jooq.Field;
+import org.jooq.Result;
+import org.jooq.Table;
+import org.jooq.impl.SchemaImpl;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * This class is generated by jOOQ.
+ */
+@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" })
+public class InformationSchema extends SchemaImpl {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The reference instance of information_schema
+ */
+ public static final InformationSchema INFORMATION_SCHEMA = new InformationSchema();
+
+ /**
+ * The table information_schema._pg_expandarray.
+ */
+ public final _PgExpandarray _PG_EXPANDARRAY = _PgExpandarray._PG_EXPANDARRAY;
+
+ /**
+ * Call information_schema._pg_expandarray.
+ */
+ public static Result<_PgExpandarrayRecord> _PG_EXPANDARRAY(
+ Configuration configuration
+ , Object[] __1
+ ) {
+ return configuration.dsl().selectFrom(dev.struchkov.network.data.jooq.schema.information_schema.tables._PgExpandarray._PG_EXPANDARRAY.call(
+ __1
+ )).fetch();
+ }
+
+ /**
+ * Get information_schema._pg_expandarray as a table.
+ */
+ public static _PgExpandarray _PG_EXPANDARRAY(
+ Object[] __1
+ ) {
+ return dev.struchkov.network.data.jooq.schema.information_schema.tables._PgExpandarray._PG_EXPANDARRAY.call(
+ __1
+ );
+ }
+
+ /**
+ * Get information_schema._pg_expandarray as a table.
+ */
+ public static _PgExpandarray _PG_EXPANDARRAY(
+ Field