Compare commits
60 commits
8b2422dd1b
...
a398641172
Author | SHA1 | Date | |
---|---|---|---|
a398641172 | |||
4ce0f70d5d | |||
568e175208 | |||
e6b7f10b87 | |||
7d4ff3ce64 | |||
1060d4d577 | |||
24adb8d0ce | |||
ceb2fe27a7 | |||
bc4a5240d7 | |||
9b6409914a | |||
391f3969c6 | |||
abbb02729a | |||
0769040fc0 | |||
5d34208625 | |||
e24f80cea5 | |||
40d33a45e6 | |||
ac43032aab | |||
12e807c5ed | |||
44c6a3b1a7 | |||
cf42778103 | |||
3c1a049dd7 | |||
5509126896 | |||
2b3d812c00 | |||
c1e2f4c4f3 | |||
aaffdda371 | |||
ed6ff54537 | |||
255a4ede3e | |||
806aee663d | |||
816eddd039 | |||
acfec04173 | |||
86de126b41 | |||
bfa5372ce4 | |||
a7388de9f5 | |||
b82355ea1a | |||
48cc667d26 | |||
6cde6438d2 | |||
c096ec17c1 | |||
656e202abd | |||
80b92dd1ea | |||
cb0633b9a2 | |||
6d837c514a | |||
3c8e2ed5e0 | |||
63b18c3f9f | |||
170300d67f | |||
56fb3bfe36 | |||
b0dabe0c61 | |||
9bf662fc78 | |||
e5194fed4f | |||
6d490ae25f | |||
a8fa525a30 | |||
0af571adf6 | |||
6f8b682f0a | |||
8323dd48ba | |||
dca11b2347 | |||
a0204e4da4 | |||
9a2861fc3f | |||
6c267b232c | |||
d5a6da0647 | |||
9914171dcb | |||
b96a544452 |
68 changed files with 6084 additions and 553 deletions
|
@ -1,3 +1,13 @@
|
|||
*
|
||||
!src/
|
||||
!pom.xml
|
||||
!ui/package*.json
|
||||
!ui/postcss.config.js
|
||||
!ui/tailwind.config.js
|
||||
!ui/env.d..ts
|
||||
!ui/vite.config.ts
|
||||
!ui/tsconfig.json
|
||||
!ui/tsconfig.node.json
|
||||
!ui/index.html
|
||||
!ui/public/
|
||||
!ui/src/
|
14
Dockerfile
14
Dockerfile
|
@ -1,4 +1,4 @@
|
|||
# Stage 1: Build the application
|
||||
# Stage 1: Build java application
|
||||
FROM docker.io/maven:3.9-eclipse-temurin-19 AS maven
|
||||
WORKDIR /app
|
||||
COPY pom.xml .
|
||||
|
@ -7,9 +7,19 @@ RUN mvn package
|
|||
COPY src/ /app/src/
|
||||
RUN mvn package
|
||||
|
||||
# Stage 2: Create the jlink app
|
||||
# Stage 2: Build vuejs application
|
||||
FROM docker.io/node:18-slim AS vuejs
|
||||
WORKDIR /app
|
||||
COPY ./ui/package* .
|
||||
RUN npm ci
|
||||
COPY ./ui .
|
||||
RUN npm run build-only
|
||||
|
||||
# Stage 3: Create the jlink app
|
||||
FROM docker.io/eclipse-temurin:19-jdk
|
||||
WORKDIR /app
|
||||
COPY --from=maven /app/target/dchat-*-fat.jar /app/dchat.jar
|
||||
COPY --from=vuejs /app/dist /app/ui/dist
|
||||
|
||||
EXPOSE 8080
|
||||
CMD ["java", "--enable-preview", "-jar", "/app/dchat.jar"]
|
||||
|
|
|
@ -22,12 +22,6 @@ and navigate into it.
|
|||
Change the environment variable inside the `docker-compose.yml`.
|
||||
|
||||
```shell
|
||||
podman-compose pull
|
||||
podman-compose start db
|
||||
podman cp schema.sql dchat_db_1:/schema.sql
|
||||
podman exec -it dchat_db_1 /bin/bash
|
||||
psql --user dchat dchat < schema.sql
|
||||
exit
|
||||
podman-compose up -d
|
||||
```
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@ services:
|
|||
- POSTGRES_PASSWORD=<postgres-password>
|
||||
- POSTGRES_URL=jdbc:postgresql://db:5432/dchat
|
||||
- API_PORT=8080
|
||||
- API_USERNAME=<api-username>
|
||||
- API_PASSWORD=<api-password>
|
||||
db:
|
||||
image: docker.io/postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -40,7 +40,7 @@
|
|||
<dependency>
|
||||
<groupId>io.javalin</groupId>
|
||||
<artifactId>javalin</artifactId>
|
||||
<version>5.4.2</version>
|
||||
<version>5.5.0</version>
|
||||
</dependency>
|
||||
<!-- logging -->
|
||||
<dependency>
|
||||
|
|
36
src/main/java/de/hhhammer/dchat/DBMigrator.java
Normal file
36
src/main/java/de/hhhammer/dchat/DBMigrator.java
Normal file
|
@ -0,0 +1,36 @@
|
|||
package de.hhhammer.dchat;
|
||||
|
||||
import de.hhhammer.dchat.migration.DBMigrationException;
|
||||
import de.hhhammer.dchat.migration.MigrationExecutor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class DBMigrator implements Runnable {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DBMigrator.class);
|
||||
private final MigrationExecutor migrationExecutor;
|
||||
private final String resourcePath;
|
||||
|
||||
public DBMigrator(MigrationExecutor migrationExecutor, String resourcePath) {
|
||||
this.migrationExecutor = migrationExecutor;
|
||||
this.resourcePath = resourcePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("Starting db migration");
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
try (InputStream inputStream = classLoader.getResourceAsStream(this.resourcePath)) {
|
||||
if (inputStream == null) {
|
||||
logger.error("Migration file not found: " + resourcePath);
|
||||
throw new RuntimeException("Migration file not found");
|
||||
}
|
||||
migrationExecutor.migrate(inputStream);
|
||||
} catch (IOException | DBMigrationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
logger.info("Finished migration");
|
||||
}
|
||||
}
|
|
@ -56,7 +56,7 @@ public class DiscordBot implements Runnable {
|
|||
if (server.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
var tokens = this.serverDBService.tokensOfLast30Days(server.get().getId());
|
||||
var tokens = this.serverDBService.tokensOfLast30Days(String.valueOf(server.get().getId()));
|
||||
interactionOriginalResponseUpdater.setContent("" + tokens).update();
|
||||
});
|
||||
return;
|
||||
|
|
|
@ -2,22 +2,26 @@ package de.hhhammer.dchat;
|
|||
|
||||
import de.hhhammer.dchat.db.ServerDBService;
|
||||
import de.hhhammer.dchat.db.UserDBService;
|
||||
import de.hhhammer.dchat.migration.MigrationExecutor;
|
||||
import de.hhhammer.dchat.openai.ChatGPTService;
|
||||
import de.hhhammer.dchat.web.JavalinConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class Main {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Main.class);
|
||||
private static final String DB_MIGRATION_PATH = "db/schema.sql";
|
||||
private final DiscordBot discordBot;
|
||||
private final WebAPI webAPI;
|
||||
private final DBMigrator dbMigrator;
|
||||
|
||||
public Main(DiscordBot discordBot, WebAPI webAPI) {
|
||||
public Main(DiscordBot discordBot, WebAPI webAPI, DBMigrator dbMigrator) {
|
||||
this.discordBot = discordBot;
|
||||
this.webAPI = webAPI;
|
||||
this.dbMigrator = dbMigrator;
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,13 +43,8 @@ public class Main {
|
|||
logger.error("Missing environment variables: POSTGRES_USER and/or POSTGRES_PASSWORD and/or POSTGRES_URL");
|
||||
System.exit(1);
|
||||
}
|
||||
String apiUsername = System.getenv("API_USERNAME");
|
||||
String apiPassword = System.getenv("API_PASSWORD");
|
||||
if (apiUsername == null || apiPassword == null) {
|
||||
logger.error("Missing environment variables: API_USERNAME and/or API_PASSWORD");
|
||||
System.exit(1);
|
||||
}
|
||||
String apiPortStr = System.getenv("PAI_PORT") != null ? System.getenv("API_PORT") : "8080";
|
||||
|
||||
String apiPortStr = System.getenv("API_PORT") != null ? System.getenv("API_PORT") : "8080";
|
||||
int apiPort = Integer.parseInt(apiPortStr);
|
||||
|
||||
var chatGPTService = new ChatGPTService(openaiApiKey, HttpClient.newHttpClient());
|
||||
|
@ -53,16 +52,20 @@ public class Main {
|
|||
var userDBService = new UserDBService(postgresUrl, postgresUser, postgresPassword);
|
||||
|
||||
var discordBot = new DiscordBot(serverDBService, userDBService, chatGPTService, discordApiKey);
|
||||
var javalinConfig = new JavalinConfig(apiPort, apiUsername, apiPassword);
|
||||
var webAPI = new WebAPI(serverDBService, userDBService, javalinConfig);
|
||||
new Main(discordBot, webAPI).run();
|
||||
var webAPI = new WebAPI(serverDBService, userDBService, apiPort);
|
||||
var dbMigrator = new DBMigrator(new MigrationExecutor(postgresUrl, postgresUser, postgresPassword), DB_MIGRATION_PATH);
|
||||
new Main(discordBot, webAPI, dbMigrator).run();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
logger.info("Starting services...");
|
||||
try (var executor = Executors.newFixedThreadPool(2)) {
|
||||
var migrationResult = executor.submit(dbMigrator);
|
||||
migrationResult.get();
|
||||
executor.submit(discordBot);
|
||||
executor.submit(webAPI);
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,13 +2,11 @@ package de.hhhammer.dchat;
|
|||
|
||||
import de.hhhammer.dchat.db.ServerDBService;
|
||||
import de.hhhammer.dchat.db.UserDBService;
|
||||
import de.hhhammer.dchat.web.JavalinConfig;
|
||||
import de.hhhammer.dchat.web.server.AllowedCrudHandler;
|
||||
import de.hhhammer.dchat.web.server.ConfigCrudHandler;
|
||||
import de.hhhammer.dchat.web.user.AllowedUserCrudHandler;
|
||||
import de.hhhammer.dchat.web.user.ConfigUserCrudHandler;
|
||||
import io.javalin.Javalin;
|
||||
import io.javalin.http.HttpStatus;
|
||||
import io.javalin.http.staticfiles.Location;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -19,12 +17,12 @@ public class WebAPI implements Runnable {
|
|||
private static final Logger logger = LoggerFactory.getLogger(WebAPI.class);
|
||||
private final ServerDBService serverDBService;
|
||||
private final UserDBService userDBService;
|
||||
private final JavalinConfig javalinConfig;
|
||||
private final int port;
|
||||
|
||||
public WebAPI(ServerDBService serverDBService, UserDBService userDBService, JavalinConfig javalinConfig) {
|
||||
public WebAPI(ServerDBService serverDBService, UserDBService userDBService, int port) {
|
||||
this.serverDBService = serverDBService;
|
||||
this.userDBService = userDBService;
|
||||
this.javalinConfig = javalinConfig;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,17 +32,10 @@ public class WebAPI implements Runnable {
|
|||
config.plugins.enableDevLogging();
|
||||
config.http.prefer405over404 = true; // return 405 instead of 404 if path is mapped to different HTTP method
|
||||
config.http.defaultContentType = "application/json";
|
||||
config.accessManager((handler, context, set) -> {
|
||||
var creds = context.basicAuthCredentials();
|
||||
if (creds == null) {
|
||||
context.status(HttpStatus.UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
if (!javalinConfig.username().equals(creds.getUsername()) || !javalinConfig.password().equals(creds.getPassword())) {
|
||||
context.status(HttpStatus.UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
handler.handle(context);
|
||||
config.staticFiles.add(staticFileConfig -> {
|
||||
staticFileConfig.hostedPath = "/";
|
||||
staticFileConfig.location = Location.EXTERNAL;
|
||||
staticFileConfig.directory = "./ui/dist/";
|
||||
});
|
||||
});
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
|
@ -55,22 +46,28 @@ public class WebAPI implements Runnable {
|
|||
event.serverStopping(() -> logger.info("Stopping web service"));
|
||||
event.serverStopped(() -> logger.info("Stopped web service"));
|
||||
});
|
||||
|
||||
app.get("/", ctx -> ctx.result("""
|
||||
{ "message": "Hello World"}
|
||||
"""));
|
||||
app.before(ctx -> {
|
||||
ctx.header("Access-Control-Allow-Origin", "*");
|
||||
ctx.header("Access-Control-Allow-Methods", "*");
|
||||
});
|
||||
app.after(ctx -> {
|
||||
if (!ctx.path().startsWith("/api") && (ctx.status().equals(HttpStatus.NOT_FOUND) || ctx.status().equals(HttpStatus.METHOD_NOT_ALLOWED))) {
|
||||
ctx.redirect("/index.html");
|
||||
}
|
||||
});
|
||||
app.options("*", ctx -> ctx.status(HttpStatus.OK));
|
||||
|
||||
app.routes(() -> {
|
||||
path("server", () -> {
|
||||
crud("allowed/{id}", new AllowedCrudHandler(this.serverDBService));
|
||||
crud("configs/{id}", new ConfigCrudHandler(this.serverDBService));
|
||||
});
|
||||
path("user", () -> {
|
||||
crud("allowed/{id}", new AllowedUserCrudHandler(this.userDBService));
|
||||
crud("configs/{id}", new ConfigUserCrudHandler(this.userDBService));
|
||||
path("api", () -> {
|
||||
path("servers", () -> {
|
||||
crud("configs/{id}", new ConfigCrudHandler(this.serverDBService));
|
||||
});
|
||||
path("users", () -> {
|
||||
crud("configs/{id}", new ConfigUserCrudHandler(this.userDBService));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.start(this.javalinConfig.port());
|
||||
app.start(this.port);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package de.hhhammer.dchat.db;
|
||||
|
||||
import de.hhhammer.dchat.db.models.server.AllowedServer;
|
||||
import de.hhhammer.dchat.db.models.server.ServerConfig;
|
||||
import de.hhhammer.dchat.db.models.server.ServerMessage;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -25,116 +24,7 @@ public class ServerDBService {
|
|||
this.password = password;
|
||||
}
|
||||
|
||||
public boolean isAllowed(long serverId) {
|
||||
var getAllowedServerSql = """
|
||||
SELECT * FROM allowed_servers WHERE server_id = ?
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getAllowedServerSql)
|
||||
) {
|
||||
pstmt.setLong(1, serverId);
|
||||
ResultSet result = pstmt.executeQuery();
|
||||
Iterable<AllowedServer> iterable = () -> new ResultSetIterator<>(result, new AllowedServer.AllowedServerResultSetTransformer());
|
||||
return StreamSupport.stream(iterable.spliterator(), false).count() == 1;
|
||||
} catch (SQLException e) {
|
||||
logger.error("Searching for allowed server with id: " + serverId, e);
|
||||
} catch (ResultSetIteratorException e) {
|
||||
logger.error("Iterating over AllowedServer ResultSet for server with id: " + serverId, e);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Optional<AllowedServer> getAllowedBy(long id) throws DBException {
|
||||
var getAllowedServerSql = """
|
||||
SELECT * FROM allowed_servers WHERE id = ?
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getAllowedServerSql)
|
||||
) {
|
||||
pstmt.setLong(1, id);
|
||||
ResultSet result = pstmt.executeQuery();
|
||||
Iterable<AllowedServer> iterable = () -> new ResultSetIterator<>(result, new AllowedServer.AllowedServerResultSetTransformer());
|
||||
return StreamSupport.stream(iterable.spliterator(), false).findFirst();
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Not found allowed server entry with id: " + id, e);
|
||||
} catch (ResultSetIteratorException e) {
|
||||
throw new DBException("Iterating over AllowedServer ResultSet searching for id: " + id, e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<AllowedServer> getAllAllowed() throws DBException {
|
||||
var getAllowedServerSql = """
|
||||
SELECT * FROM allowed_servers
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getAllowedServerSql)
|
||||
) {
|
||||
ResultSet result = pstmt.executeQuery();
|
||||
Iterable<AllowedServer> iterable = () -> new ResultSetIterator<>(result, new AllowedServer.AllowedServerResultSetTransformer());
|
||||
return StreamSupport.stream(iterable.spliterator(), false).toList();
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Not found allowed server entries", e);
|
||||
} catch (ResultSetIteratorException e) {
|
||||
throw new DBException("Iterating over AllowedServer ResultSet", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAllowed(AllowedServer.NewAllowedServer newAllowedServer) throws DBException {
|
||||
var insertAllowedServerSql = """
|
||||
INSERT INTO allowed_servers (server_id, comment) VALUES (?, ?)
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(insertAllowedServerSql)
|
||||
) {
|
||||
pstmt.setLong(1, newAllowedServer.serverId());
|
||||
pstmt.setString(2, newAllowedServer.comment());
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
if (affectedRows == 0) {
|
||||
logger.error("No server inserted to allowed_servers with id: " + newAllowedServer.serverId());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Allowing new server with id: " + newAllowedServer.serverId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAllowed(long id, AllowedServer.NewAllowedServer newAllowedServer) throws DBException {
|
||||
var insertAllowedServerSql = """
|
||||
UPDATE allowed_servers SET comment = ?, server_id = ? WHERE id = ?
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(insertAllowedServerSql)
|
||||
) {
|
||||
pstmt.setString(1, newAllowedServer.comment());
|
||||
pstmt.setLong(2, newAllowedServer.serverId());
|
||||
pstmt.setLong(3, id);
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
if (affectedRows == 0) {
|
||||
logger.error("No comment updated on server with id: " + newAllowedServer.serverId());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Updating comment on allowed server with id: " + newAllowedServer.serverId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAllowed(long id) throws DBException {
|
||||
var insertAllowedServerSql = """
|
||||
DELETE FROM allowed_servers WHERE id = ?
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(insertAllowedServerSql)
|
||||
) {
|
||||
pstmt.setLong(1, id);
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
if (affectedRows == 0) {
|
||||
logger.error("No server deleted with id: " + id);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Deleting allowed server with id: " + id, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<ServerConfig> getConfig(long serverId) {
|
||||
public Optional<ServerConfig> getConfig(String serverId) {
|
||||
var getServerConfig = """
|
||||
SELECT * FROM server_configs WHERE server_id = ?
|
||||
""";
|
||||
|
@ -142,7 +32,7 @@ public class ServerDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getServerConfig)
|
||||
) {
|
||||
pstmt.setLong(1, serverId);
|
||||
pstmt.setString(1, serverId);
|
||||
ResultSet resultSet = pstmt.executeQuery();
|
||||
Iterable<ServerConfig> iterable = () -> new ResultSetIterator<>(resultSet, new ServerConfig.ServerConfigResultSetTransformer());
|
||||
return StreamSupport.stream(iterable.spliterator(), false).findFirst();
|
||||
|
@ -199,7 +89,7 @@ public class ServerDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getServerConfig)
|
||||
) {
|
||||
pstmt.setLong(1, newServerConfig.serverId());
|
||||
pstmt.setString(1, newServerConfig.serverId());
|
||||
pstmt.setString(2, newServerConfig.systemMessage());
|
||||
pstmt.setInt(3, newServerConfig.rateLimit());
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
|
@ -221,7 +111,7 @@ public class ServerDBService {
|
|||
) {
|
||||
pstmt.setString(1, newServerConfig.systemMessage());
|
||||
pstmt.setInt(2, newServerConfig.rateLimit());
|
||||
pstmt.setLong(3, newServerConfig.serverId());
|
||||
pstmt.setString(3, newServerConfig.serverId());
|
||||
pstmt.setLong(4, id);
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
if (affectedRows == 0) {
|
||||
|
@ -250,7 +140,7 @@ public class ServerDBService {
|
|||
}
|
||||
}
|
||||
|
||||
public int countMessagesInLastMinute(long serverId) {
|
||||
public int countMessagesInLastMinute(String serverId) {
|
||||
var getServerConfig = """
|
||||
SELECT count(*) FROM server_messages WHERE server_id = ? AND time <= ? and time >= ?
|
||||
""";
|
||||
|
@ -258,7 +148,7 @@ public class ServerDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getServerConfig)
|
||||
) {
|
||||
pstmt.setLong(1, serverId);
|
||||
pstmt.setString(1, serverId);
|
||||
var now = Instant.now();
|
||||
pstmt.setTimestamp(2, Timestamp.from(now));
|
||||
pstmt.setTimestamp(3, Timestamp.from(now.minus(1, ChronoUnit.MINUTES)));
|
||||
|
@ -282,7 +172,7 @@ public class ServerDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getServerConfig)
|
||||
) {
|
||||
pstmt.setLong(1, serverMessage.serverId());
|
||||
pstmt.setString(1, serverMessage.serverId());
|
||||
pstmt.setLong(2, serverMessage.userId());
|
||||
pstmt.setInt(3, serverMessage.tokens());
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
|
@ -294,7 +184,7 @@ public class ServerDBService {
|
|||
}
|
||||
}
|
||||
|
||||
public long tokensOfLast30Days(long serverId) {
|
||||
public long tokensOfLast30Days(String serverId) {
|
||||
var countTokensOfLast30Days = """
|
||||
SELECT sum(tokens) FROM server_messages WHERE server_id = ? AND time < ? AND time >= ?
|
||||
""";
|
||||
|
@ -302,7 +192,7 @@ public class ServerDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(countTokensOfLast30Days)
|
||||
) {
|
||||
pstmt.setLong(1, serverId);
|
||||
pstmt.setString(1, serverId);
|
||||
var now = Instant.now();
|
||||
pstmt.setTimestamp(2, Timestamp.from(now));
|
||||
pstmt.setTimestamp(3, Timestamp.from(now.minus(30, ChronoUnit.DAYS)));
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package de.hhhammer.dchat.db;
|
||||
|
||||
import de.hhhammer.dchat.db.models.user.AllowedUser;
|
||||
import de.hhhammer.dchat.db.models.user.UserConfig;
|
||||
import de.hhhammer.dchat.db.models.user.UserMessage;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -25,115 +24,7 @@ public class UserDBService {
|
|||
this.password = password;
|
||||
}
|
||||
|
||||
public boolean isAllowed(long userId) {
|
||||
var getAllowedServerSql = """
|
||||
SELECT * FROM allowed_users WHERE user_id = ?
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getAllowedServerSql)
|
||||
) {
|
||||
pstmt.setLong(1, userId);
|
||||
ResultSet result = pstmt.executeQuery();
|
||||
Iterable<AllowedUser> iterable = () -> new ResultSetIterator<>(result, new AllowedUser.AllowedUserResultSetTransformer());
|
||||
return StreamSupport.stream(iterable.spliterator(), false).count() == 1;
|
||||
} catch (SQLException e) {
|
||||
logger.error("Searching for allowed user with id: " + userId, e);
|
||||
} catch (ResultSetIteratorException e) {
|
||||
logger.error("Iterating over AllowedServer ResultSet for user with id: " + userId, e);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Optional<AllowedUser> getAllowedBy(long id) throws DBException {
|
||||
var getAllowedServerSql = """
|
||||
SELECT * FROM allowed_users WHERE id = ?
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getAllowedServerSql)
|
||||
) {
|
||||
pstmt.setLong(1, id);
|
||||
ResultSet result = pstmt.executeQuery();
|
||||
Iterable<AllowedUser> iterable = () -> new ResultSetIterator<>(result, new AllowedUser.AllowedUserResultSetTransformer());
|
||||
return StreamSupport.stream(iterable.spliterator(), false).findFirst();
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Searching for allowed with id: " + id, e);
|
||||
} catch (ResultSetIteratorException e) {
|
||||
throw new DBException("Iterating over AllowedServer ResultSet with id: " + id, e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<AllowedUser> getAllAllowed() throws DBException {
|
||||
var getAllowedServerSql = """
|
||||
SELECT * FROM allowed_users
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getAllowedServerSql)
|
||||
) {
|
||||
ResultSet result = pstmt.executeQuery();
|
||||
Iterable<AllowedUser> iterable = () -> new ResultSetIterator<>(result, new AllowedUser.AllowedUserResultSetTransformer());
|
||||
return StreamSupport.stream(iterable.spliterator(), false).toList();
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Searching all allowed users", e);
|
||||
} catch (ResultSetIteratorException e) {
|
||||
throw new DBException("Iterating over all AllowedServer ResultSet ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAllowed(AllowedUser.NewAllowedUser newAllowedUser) throws DBException {
|
||||
var insertAllowedServerSql = """
|
||||
INSERT INTO allowed_users (user_id, comment) VALUES (?, ?)
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(insertAllowedServerSql)
|
||||
) {
|
||||
pstmt.setLong(1, newAllowedUser.userId());
|
||||
pstmt.setString(2, newAllowedUser.comment());
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
if (affectedRows == 0) {
|
||||
logger.error("No server inserted to allowed_users with id: " + newAllowedUser.userId());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Allowing new user with id: " + newAllowedUser.userId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAllowed(long id, AllowedUser.NewAllowedUser newAllowedUser) throws DBException {
|
||||
var insertAllowedServerSql = """
|
||||
UPDATE allowed_users SET comment = ? WHERE id = ?
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(insertAllowedServerSql)
|
||||
) {
|
||||
pstmt.setString(1, newAllowedUser.comment());
|
||||
pstmt.setLong(2, id);
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
if (affectedRows == 0) {
|
||||
logger.error("No comment updated on user with id: " + newAllowedUser.userId());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Updating comment on allowed user with id: " + id, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAllowed(long id) throws DBException {
|
||||
var insertAllowedServerSql = """
|
||||
DELETE FROM allowed_users WHERE id = ?
|
||||
""";
|
||||
try (Connection con = DriverManager.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(insertAllowedServerSql)
|
||||
) {
|
||||
pstmt.setLong(1, id);
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
if (affectedRows == 0) {
|
||||
logger.error("No user deleted with id: " + id);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Deleting allowed user with id: " + id, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<UserConfig> getConfig(long userId) {
|
||||
public Optional<UserConfig> getConfig(String userId) {
|
||||
var getServerConfig = """
|
||||
SELECT * FROM user_configs WHERE user_id = ?
|
||||
""";
|
||||
|
@ -141,7 +32,7 @@ public class UserDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getServerConfig)
|
||||
) {
|
||||
pstmt.setLong(1, userId);
|
||||
pstmt.setString(1, userId);
|
||||
ResultSet resultSet = pstmt.executeQuery();
|
||||
Iterable<UserConfig> iterable = () -> new ResultSetIterator<>(resultSet, new UserConfig.UserConfigResultSetTransformer());
|
||||
return StreamSupport.stream(iterable.spliterator(), false).findFirst();
|
||||
|
@ -199,7 +90,7 @@ public class UserDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getServerConfig)
|
||||
) {
|
||||
pstmt.setLong(1, newUserConfig.userId());
|
||||
pstmt.setString(1, newUserConfig.userId());
|
||||
pstmt.setString(2, newUserConfig.systemMessage());
|
||||
pstmt.setInt(3, newUserConfig.contextLength());
|
||||
pstmt.setInt(4, newUserConfig.rateLimit());
|
||||
|
@ -214,7 +105,7 @@ public class UserDBService {
|
|||
|
||||
public void updateConfig(long id, UserConfig.NewUserConfig newUserConfig) throws DBException {
|
||||
var getServerConfig = """
|
||||
UPDATE user_configs SET system_message = ?, context_length = ?, rate_limit = ? WHERE id = ?
|
||||
UPDATE user_configs SET system_message = ?, context_length = ?, rate_limit = ?, user_id = ? WHERE id = ?
|
||||
""";
|
||||
try (Connection con = DriverManager
|
||||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
|
@ -223,7 +114,8 @@ public class UserDBService {
|
|||
pstmt.setString(1, newUserConfig.systemMessage());
|
||||
pstmt.setInt(2, newUserConfig.rateLimit());
|
||||
pstmt.setLong(3, newUserConfig.contextLength());
|
||||
pstmt.setLong(4, id);
|
||||
pstmt.setString(4, newUserConfig.userId());
|
||||
pstmt.setLong(5, id);
|
||||
int affectedRows = pstmt.executeUpdate();
|
||||
if (affectedRows == 0) {
|
||||
logger.error("No config update with id: " + id);
|
||||
|
@ -251,7 +143,7 @@ public class UserDBService {
|
|||
}
|
||||
}
|
||||
|
||||
public int countMessagesInLastMinute(long userId) {
|
||||
public int countMessagesInLastMinute(String userId) {
|
||||
var getServerConfig = """
|
||||
SELECT count(*) FROM user_messages WHERE user_id = ? AND time <= ? and time >= ?
|
||||
""";
|
||||
|
@ -259,7 +151,7 @@ public class UserDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getServerConfig)
|
||||
) {
|
||||
pstmt.setLong(1, userId);
|
||||
pstmt.setString(1, userId);
|
||||
var now = Instant.now();
|
||||
pstmt.setTimestamp(2, Timestamp.from(now));
|
||||
pstmt.setTimestamp(3, Timestamp.from(now.minus(1, ChronoUnit.MINUTES)));
|
||||
|
@ -283,7 +175,7 @@ public class UserDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(getServerConfig)
|
||||
) {
|
||||
pstmt.setLong(1, newUserMessage.userId());
|
||||
pstmt.setString(1, newUserMessage.userId());
|
||||
pstmt.setString(2, newUserMessage.question());
|
||||
pstmt.setString(3, newUserMessage.answer());
|
||||
pstmt.setInt(4, newUserMessage.tokens());
|
||||
|
@ -296,7 +188,7 @@ public class UserDBService {
|
|||
}
|
||||
}
|
||||
|
||||
public long tokensOfLast30Days(long userId) {
|
||||
public long tokensOfLast30Days(String userId) {
|
||||
var countTokensOfLast30Days = """
|
||||
SELECT sum(tokens) FROM user_messages WHERE user_id = ? AND time < ? AND time >= ?
|
||||
""";
|
||||
|
@ -304,7 +196,7 @@ public class UserDBService {
|
|||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
PreparedStatement pstmt = con.prepareStatement(countTokensOfLast30Days)
|
||||
) {
|
||||
pstmt.setLong(1, userId);
|
||||
pstmt.setString(1, userId);
|
||||
var now = Instant.now();
|
||||
pstmt.setTimestamp(2, Timestamp.from(now));
|
||||
pstmt.setTimestamp(3, Timestamp.from(now.minus(30, ChronoUnit.DAYS)));
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package de.hhhammer.dchat.db.models.server;
|
||||
|
||||
import de.hhhammer.dchat.db.ResultSetTransformer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
|
||||
public record AllowedServer(long id, long serverId, Instant time, @Nullable String comment) {
|
||||
|
||||
public static class AllowedServerResultSetTransformer implements ResultSetTransformer<AllowedServer> {
|
||||
@Override
|
||||
public AllowedServer transform(ResultSet resultSet) throws SQLException {
|
||||
var id = resultSet.getLong("id");
|
||||
var serverId = resultSet.getLong("server_id");
|
||||
var time = resultSet.getTimestamp("time").toInstant();
|
||||
var comment = resultSet.getString("comment");
|
||||
return new AllowedServer(id, serverId, time, comment);
|
||||
}
|
||||
}
|
||||
|
||||
public record NewAllowedServer(long serverId, @Nullable String comment) {
|
||||
}
|
||||
}
|
|
@ -1,25 +1,29 @@
|
|||
package de.hhhammer.dchat.db.models.server;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import de.hhhammer.dchat.db.ResultSetTransformer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
|
||||
public record ServerConfig(long id, long serverId, String systemMessage, int rateLimit) {
|
||||
public record ServerConfig(long id, String serverId, String systemMessage, int rateLimit, Instant time) {
|
||||
|
||||
public static class ServerConfigResultSetTransformer implements ResultSetTransformer<ServerConfig> {
|
||||
|
||||
@Override
|
||||
public ServerConfig transform(ResultSet resultSet) throws SQLException {
|
||||
var id = resultSet.getLong("id");
|
||||
var serverId = resultSet.getLong("server_id");
|
||||
var serverId = resultSet.getString("server_id");
|
||||
var systemMessage = resultSet.getString("system_message");
|
||||
var rateLimit = resultSet.getInt("rate_limit");
|
||||
return new ServerConfig(id, serverId, systemMessage, rateLimit);
|
||||
var time = resultSet.getTimestamp("time").toInstant();
|
||||
return new ServerConfig(id, serverId, systemMessage, rateLimit, time);
|
||||
}
|
||||
}
|
||||
|
||||
public record NewServerConfig(long serverId, String systemMessage, @Nullable int rateLimit) {
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record NewServerConfig(String serverId, String systemMessage, @Nullable int rateLimit) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
package de.hhhammer.dchat.db.models.server;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import de.hhhammer.dchat.db.ResultSetTransformer;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
|
||||
public record ServerMessage(long id, long serverId, long userId, int tokens, Instant time) {
|
||||
public record ServerMessage(long id, String serverId, long userId, int tokens, Instant time) {
|
||||
|
||||
public static class ServerMessageResultSetTransformer implements ResultSetTransformer<ServerMessage> {
|
||||
|
||||
@Override
|
||||
public ServerMessage transform(ResultSet resultSet) throws SQLException {
|
||||
var id = resultSet.getLong("id");
|
||||
var serverId = resultSet.getLong("server_id");
|
||||
var serverId = resultSet.getString("server_id");
|
||||
var userId = resultSet.getLong("user_id");
|
||||
var tokens = resultSet.getInt("tokens");
|
||||
var time = resultSet.getTimestamp("time").toInstant();
|
||||
|
@ -21,6 +22,7 @@ public record ServerMessage(long id, long serverId, long userId, int tokens, Ins
|
|||
}
|
||||
}
|
||||
|
||||
public record NewServerMessage(long serverId, long userId, int tokens) {
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record NewServerMessage(String serverId, long userId, int tokens) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package de.hhhammer.dchat.db.models.user;
|
||||
|
||||
import de.hhhammer.dchat.db.ResultSetTransformer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
|
||||
public record AllowedUser(long id, long userId, Instant time, @Nullable String comment) {
|
||||
|
||||
public static class AllowedUserResultSetTransformer implements ResultSetTransformer<AllowedUser> {
|
||||
@Override
|
||||
public AllowedUser transform(ResultSet resultSet) throws SQLException {
|
||||
var id = resultSet.getLong("id");
|
||||
var userId = resultSet.getLong("user_id");
|
||||
var time = resultSet.getTimestamp("time").toInstant();
|
||||
var comment = resultSet.getString("comment");
|
||||
return new AllowedUser(id, userId, time, comment);
|
||||
}
|
||||
}
|
||||
|
||||
public record NewAllowedUser(long userId, @Nullable String comment) {
|
||||
}
|
||||
}
|
|
@ -1,27 +1,31 @@
|
|||
package de.hhhammer.dchat.db.models.user;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import de.hhhammer.dchat.db.ResultSetTransformer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
|
||||
public record UserConfig(long id, long userId, String systemMessage, int contextLength, int rateLimit) {
|
||||
public record UserConfig(long id, String userId, String systemMessage, int contextLength, int rateLimit, Instant time) {
|
||||
|
||||
public static class UserConfigResultSetTransformer implements ResultSetTransformer<UserConfig> {
|
||||
|
||||
@Override
|
||||
public UserConfig transform(ResultSet resultSet) throws SQLException {
|
||||
var id = resultSet.getLong("id");
|
||||
var userId = resultSet.getLong("user_id");
|
||||
var userId = resultSet.getString("user_id");
|
||||
var systemMessage = resultSet.getString("system_message");
|
||||
var contextLength = resultSet.getInt("context_length");
|
||||
var rateLimit = resultSet.getInt("rate_limit");
|
||||
return new UserConfig(id, userId, systemMessage, contextLength, rateLimit);
|
||||
var time = resultSet.getTimestamp("time").toInstant();
|
||||
return new UserConfig(id, userId, systemMessage, contextLength, rateLimit, time);
|
||||
}
|
||||
}
|
||||
|
||||
public record NewUserConfig(long userId, String systemMessage, @Nullable int contextLength,
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record NewUserConfig(String userId, String systemMessage, @Nullable int contextLength,
|
||||
@Nullable int rateLimit) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
package de.hhhammer.dchat.db.models.user;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import de.hhhammer.dchat.db.ResultSetTransformer;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
|
||||
public record UserMessage(long id, long userId, String question, String answer, int tokens, Instant time) {
|
||||
public record UserMessage(long id, String userId, String question, String answer, int tokens, Instant time) {
|
||||
|
||||
public static class UserMessageResultSetTransformer implements ResultSetTransformer<UserMessage> {
|
||||
|
||||
@Override
|
||||
public UserMessage transform(ResultSet resultSet) throws SQLException {
|
||||
var id = resultSet.getLong("id");
|
||||
var userId = resultSet.getLong("user_id");
|
||||
var userId = resultSet.getString("user_id");
|
||||
var question = resultSet.getString("question");
|
||||
var answer = resultSet.getString("answer");
|
||||
var tokens = resultSet.getInt("tokens");
|
||||
|
@ -22,6 +23,7 @@ public record UserMessage(long id, long userId, String question, String answer,
|
|||
}
|
||||
}
|
||||
|
||||
public record NewUserMessage(long userId, String question, String answer, int tokens) {
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record NewUserMessage(String userId, String question, String answer, int tokens) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public class MessageCreateHandler implements MessageCreateListener {
|
|||
return;
|
||||
}
|
||||
if (this.messageHandler.exceedsRate(event)) {
|
||||
event.getChannel().sendMessage("Rate limit hit - cooling down now...");
|
||||
event.getChannel().sendMessage("Rate limit hit - cooling down...");
|
||||
return;
|
||||
}
|
||||
this.messageHandler.handle(event);
|
||||
|
|
|
@ -29,6 +29,8 @@ public class ServerMessageHandler implements MessageHandler {
|
|||
@Override
|
||||
public void handle(MessageCreateEvent event) {
|
||||
String content = extractContent(event);
|
||||
var serverId = event.getServer().get().getId();
|
||||
var systemMessage = this.serverDBService.getConfig(String.valueOf(serverId)).get().systemMessage();
|
||||
var request = event.getMessage().getType() == MessageType.REPLY ?
|
||||
new ChatGPTRequestBuilder()
|
||||
.contextRequest(event.getMessage()
|
||||
|
@ -37,8 +39,8 @@ public class ServerMessageHandler implements MessageHandler {
|
|||
.flatMap(m -> m)
|
||||
.map(Message::getReadableContent)
|
||||
.stream().toList(),
|
||||
content) :
|
||||
new ChatGPTRequestBuilder().simpleRequest(content);
|
||||
content, systemMessage) :
|
||||
new ChatGPTRequestBuilder().simpleRequest(content, systemMessage);
|
||||
try {
|
||||
var response = this.chatGPTService.submit(request);
|
||||
if (response.choices().size() < 1) {
|
||||
|
@ -56,21 +58,22 @@ public class ServerMessageHandler implements MessageHandler {
|
|||
|
||||
@Override
|
||||
public boolean isAllowed(MessageCreateEvent event) {
|
||||
if (event.getServer().isPresent()) {
|
||||
var serverId = event.getServer().get().getId();
|
||||
var allowed = this.serverDBService.isAllowed(serverId);
|
||||
if (!allowed) {
|
||||
logger.debug("Not allowed with id: " + serverId);
|
||||
}
|
||||
return allowed;
|
||||
if (event.getServer().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// only support server messages
|
||||
return false;
|
||||
|
||||
var serverId = event.getServer().get().getId();
|
||||
var config = this.serverDBService.getConfig(String.valueOf(serverId));
|
||||
if (config.isEmpty()) {
|
||||
logger.debug("Not allowed with id: " + serverId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exceedsRate(MessageCreateEvent event) {
|
||||
var serverId = event.getServer().get().getId();
|
||||
var serverId = String.valueOf(event.getServer().get().getId());
|
||||
var config = this.serverDBService.getConfig(serverId);
|
||||
if (config.isEmpty()) {
|
||||
logger.error("Missing configuration for server with id: " + serverId);
|
||||
|
@ -91,7 +94,7 @@ public class ServerMessageHandler implements MessageHandler {
|
|||
var serverId = event.getServer().map(DiscordEntity::getId).get();
|
||||
var userId = event.getMessageAuthor().getId();
|
||||
|
||||
var serverMessage = new ServerMessage.NewServerMessage(serverId, userId, tokens);
|
||||
var serverMessage = new ServerMessage.NewServerMessage(String.valueOf(serverId), userId, tokens);
|
||||
this.serverDBService.addMessage(serverMessage);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,9 @@ public class UserMessageHandler implements MessageHandler {
|
|||
@Override
|
||||
public void handle(MessageCreateEvent event) {
|
||||
String content = event.getReadableMessageContent();
|
||||
var request = new ChatGPTRequestBuilder().simpleRequest(content);
|
||||
var userId = event.getMessageAuthor().getId();
|
||||
var systemMessage = this.userDBService.getConfig(String.valueOf(userId)).get().systemMessage();
|
||||
var request = new ChatGPTRequestBuilder().simpleRequest(content, systemMessage);
|
||||
try {
|
||||
var response = this.chatGPTService.submit(request);
|
||||
if (response.choices().size() < 1) {
|
||||
|
@ -47,16 +49,17 @@ public class UserMessageHandler implements MessageHandler {
|
|||
}
|
||||
|
||||
var userId = event.getMessageAuthor().getId();
|
||||
var allowed = this.userDBService.isAllowed(userId);
|
||||
if (!allowed) {
|
||||
var config = this.userDBService.getConfig(String.valueOf(userId));
|
||||
if (config.isEmpty()) {
|
||||
logger.debug("Not allowed with id: " + userId);
|
||||
return false;
|
||||
}
|
||||
return allowed;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exceedsRate(MessageCreateEvent event) {
|
||||
var userId = event.getMessageAuthor().getId();
|
||||
var userId = String.valueOf(event.getMessageAuthor().getId());
|
||||
var config = this.userDBService.getConfig(userId);
|
||||
if (config.isEmpty()) {
|
||||
logger.error("Missing configuration for userId with id: " + userId);
|
||||
|
@ -76,7 +79,7 @@ public class UserMessageHandler implements MessageHandler {
|
|||
private void logUserMessage(MessageCreateEvent event, String question, String answer, int tokens) {
|
||||
var userId = event.getMessageAuthor().getId();
|
||||
|
||||
var userMessage = new UserMessage.NewUserMessage(userId, question, answer, tokens);
|
||||
var userMessage = new UserMessage.NewUserMessage(String.valueOf(userId), question, answer, tokens);
|
||||
this.userDBService.addMessage(userMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package de.hhhammer.dchat.migration;
|
||||
|
||||
public class DBMigrationException extends Exception {
|
||||
public DBMigrationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package de.hhhammer.dchat.migration;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class MigrationExecutor {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MigrationExecutor.class);
|
||||
private final String jdbcConnectionString;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public MigrationExecutor(String jdbcConnectionString, String username, String password) {
|
||||
this.jdbcConnectionString = jdbcConnectionString;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void migrate(InputStream input) throws DBMigrationException {
|
||||
try (Connection con = DriverManager
|
||||
.getConnection(this.jdbcConnectionString, this.username, this.password);
|
||||
Statement stmp = con.createStatement();
|
||||
) {
|
||||
String content = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
||||
stmp.execute(content);
|
||||
} catch (SQLException | IOException e) {
|
||||
throw new DBMigrationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -7,14 +7,11 @@ import java.util.List;
|
|||
|
||||
public class ChatGPTRequestBuilder {
|
||||
private final String model = "gpt-3.5-turbo";
|
||||
private final String systemMessage = """
|
||||
You are Jarvis, a helpful and friendly AI. People interact with you over Discord, a chatting platform. Your default language to answer is German. You format your responses in markdown and your answers don´t need to be formal.
|
||||
""";
|
||||
|
||||
public ChatGPTRequestBuilder() {
|
||||
}
|
||||
|
||||
public ChatGPTRequest simpleRequest(String content) {
|
||||
public ChatGPTRequest simpleRequest(String content, String systemMessage) {
|
||||
return new ChatGPTRequest(
|
||||
model,
|
||||
List.of(new ChatGPTRequest.Message("system", systemMessage), new ChatGPTRequest.Message("user", content)),
|
||||
|
@ -22,7 +19,7 @@ public class ChatGPTRequestBuilder {
|
|||
);
|
||||
}
|
||||
|
||||
public ChatGPTRequest contextRequest(List<String> contextMessages, String message) {
|
||||
public ChatGPTRequest contextRequest(List<String> contextMessages, String message, String systemMessage) {
|
||||
List<ChatGPTRequest.Message> messages = new ArrayList<>();
|
||||
messages.add(new ChatGPTRequest.Message("system", systemMessage));
|
||||
var context = contextMessages.stream()
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
package de.hhhammer.dchat.web;
|
||||
|
||||
public record JavalinConfig(int port, String username, String password) {
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package de.hhhammer.dchat.web.server;
|
||||
|
||||
import de.hhhammer.dchat.db.DBException;
|
||||
import de.hhhammer.dchat.db.ServerDBService;
|
||||
import de.hhhammer.dchat.db.models.server.AllowedServer;
|
||||
import io.javalin.apibuilder.CrudHandler;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.HttpStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AllowedCrudHandler implements CrudHandler {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AllowedCrudHandler.class);
|
||||
|
||||
private final ServerDBService serverDBService;
|
||||
|
||||
public AllowedCrudHandler(ServerDBService serverDBService) {
|
||||
this.serverDBService = serverDBService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(@NotNull Context context) {
|
||||
var body = context.bodyAsClass(AllowedServer.NewAllowedServer.class);
|
||||
try {
|
||||
this.serverDBService.addAllowed(body);
|
||||
} catch (DBException e) {
|
||||
logger.error("Adding new allowed server", e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
context.status(HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(@NotNull Context context, @NotNull String s) {
|
||||
try {
|
||||
this.serverDBService.deleteAllowed(Long.parseLong(s));
|
||||
context.status(HttpStatus.NO_CONTENT);
|
||||
} catch (DBException e) {
|
||||
logger.error("Deleting server with id: " + s, e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAll(@NotNull Context context) {
|
||||
try {
|
||||
var allowedServers = this.serverDBService.getAllAllowed();
|
||||
context.json(allowedServers);
|
||||
} catch (DBException e) {
|
||||
logger.error("Getting all allowed servers", e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOne(@NotNull Context context, @NotNull String s) {
|
||||
var id = Long.parseLong(s);
|
||||
try {
|
||||
var server = this.serverDBService.getAllowedBy(id);
|
||||
if (server.isEmpty()) {
|
||||
context.status(HttpStatus.NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
context.json(server.get());
|
||||
} catch (DBException e) {
|
||||
logger.error("Searching with id: " + s, e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull Context context, @NotNull String idString) {
|
||||
var newAllowedServer = context.bodyAsClass(AllowedServer.NewAllowedServer.class);
|
||||
var id = Long.parseLong(idString);
|
||||
|
||||
try {
|
||||
this.serverDBService.updateAllowed(id, newAllowedServer);
|
||||
} catch (DBException e) {
|
||||
logger.error("Updating allowed server with id: " + idString, e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package de.hhhammer.dchat.web.user;
|
||||
|
||||
import de.hhhammer.dchat.db.DBException;
|
||||
import de.hhhammer.dchat.db.UserDBService;
|
||||
import de.hhhammer.dchat.db.models.user.AllowedUser;
|
||||
import io.javalin.apibuilder.CrudHandler;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.HttpStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AllowedUserCrudHandler implements CrudHandler {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AllowedUserCrudHandler.class);
|
||||
|
||||
private final UserDBService userDBService;
|
||||
|
||||
public AllowedUserCrudHandler(UserDBService userDBService) {
|
||||
this.userDBService = userDBService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(@NotNull Context context) {
|
||||
var body = context.bodyAsClass(AllowedUser.NewAllowedUser.class);
|
||||
try {
|
||||
this.userDBService.addAllowed(body);
|
||||
} catch (DBException e) {
|
||||
logger.error("Adding new server configuration", e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
context.status(HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(@NotNull Context context, @NotNull String s) {
|
||||
try {
|
||||
this.userDBService.deleteAllowed(Long.parseLong(s));
|
||||
context.status(HttpStatus.NO_CONTENT);
|
||||
} catch (DBException e) {
|
||||
logger.error("Deleting configuration with id: " + s, e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAll(@NotNull Context context) {
|
||||
try {
|
||||
var allowedServers = this.userDBService.getAllAllowed();
|
||||
context.json(allowedServers);
|
||||
} catch (DBException e) {
|
||||
logger.error("Getting all server configs", e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOne(@NotNull Context context, @NotNull String s) {
|
||||
var id = Long.parseLong(s);
|
||||
try {
|
||||
var server = this.userDBService.getAllowedBy(id);
|
||||
if (server.isEmpty()) {
|
||||
context.status(HttpStatus.NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
context.json(server.get());
|
||||
} catch (DBException e) {
|
||||
logger.error("Searching for config with id: " + s, e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull Context context, @NotNull String idString) {
|
||||
var body = context.bodyAsClass(AllowedUser.NewAllowedUser.class);
|
||||
var id = Long.parseLong(idString);
|
||||
|
||||
try {
|
||||
this.userDBService.updateAllowed(id, body);
|
||||
} catch (DBException e) {
|
||||
logger.error("Updating allowed server with id: " + idString, e);
|
||||
context.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -86,4 +86,39 @@ BEGIN;
|
|||
ALTER TABLE user_configs
|
||||
ADD COLUMN IF NOT EXISTS context_length INT NOT NULL DEFAULT 5;
|
||||
|
||||
COMMIT
|
||||
COMMIT;
|
||||
|
||||
-- Add time to config to know when it was added
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE server_configs
|
||||
ADD COLUMN IF NOT EXISTS time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
ALTER TABLE user_configs
|
||||
ADD COLUMN IF NOT EXISTS time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- Remove obsolete allowed tables that are being replaced the configs tables
|
||||
BEGIN;
|
||||
|
||||
DROP TABLE IF EXISTS allowed_servers;
|
||||
DROP TABLE IF EXISTS allowed_users;
|
||||
|
||||
COMMIT;
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE server_configs
|
||||
ALTER COLUMN server_id TYPE text USING server_id::text;
|
||||
|
||||
ALTER TABLE server_messages
|
||||
ALTER COLUMN server_id TYPE text USING server_id::text;
|
||||
|
||||
ALTER TABLE user_configs
|
||||
ALTER COLUMN user_id TYPE text USING user_id::text;
|
||||
|
||||
ALTER TABLE user_messages
|
||||
ALTER COLUMN user_id TYPE text USING user_id::text;
|
||||
|
||||
COMMIT;
|
12
ui/.containerignore
Normal file
12
ui/.containerignore
Normal file
|
@ -0,0 +1,12 @@
|
|||
*
|
||||
!Caddyfile
|
||||
!package*.json
|
||||
!postcss.config.js
|
||||
!tailwind.config.js
|
||||
!env.d..ts
|
||||
!vite.config.ts
|
||||
!tsconfig.json
|
||||
!tsconfig.node.json
|
||||
!index.html
|
||||
!public/
|
||||
!src/
|
28
ui/.gitignore
vendored
Normal file
28
ui/.gitignore
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
3
ui/.vscode/extensions.json
vendored
Normal file
3
ui/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
11
ui/Caddyfile
Normal file
11
ui/Caddyfile
Normal file
|
@ -0,0 +1,11 @@
|
|||
{$SITE_ADDRESS} {
|
||||
basicauth * {
|
||||
{$AUTH_PASSWORD} {$AUTH_PASSWORD}
|
||||
}
|
||||
|
||||
encode zstd gzip
|
||||
|
||||
root * /app
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
}
|
16
ui/Containerfile
Normal file
16
ui/Containerfile
Normal file
|
@ -0,0 +1,16 @@
|
|||
FROM docker.io/node:18-slim AS build
|
||||
WORKDIR /app
|
||||
COPY ./package* .
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build-only
|
||||
|
||||
FROM docker.io/caddy:2-alpine
|
||||
|
||||
WORKDIR /app
|
||||
COPY ./Caddyfile /
|
||||
COPY --from=build /app/dist /app
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["caddy", "run", "--config", "/Caddyfile"]
|
40
ui/README.md
Normal file
40
ui/README.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
# dchat-ui
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||
|
||||
1. Disable the built-in TypeScript Extension
|
||||
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
31
ui/build.sh
Executable file
31
ui/build.sh
Executable file
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
REPO="git.hhhammer.de/hamburghammer/dchat-ui"
|
||||
AUTHORS="Augusto Dwenger J. <dwenger@posteo.de>"
|
||||
URL="https://${REPO}"
|
||||
VENDOR="hamburghammer"
|
||||
TITLE="dchat-ui"
|
||||
DESCRIPTION="The Web-UI for dchat a Discord bot to chat with ChatGPT from OpenAI"
|
||||
CREATED=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
REVISION=$(git rev-parse HEAD)
|
||||
|
||||
IMAGE_LATEST="${REPO}:latest"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
podman build \
|
||||
$DOCKER_BUILD_ARGS \
|
||||
-t $IMAGE_LATEST \
|
||||
--label "org.opencontainers.image.revision=${REVISION}" \
|
||||
--label "org.opencontainers.image.version=${TAG}" \
|
||||
--label "org.opencontainers.image.authors=${AUTHORS}" \
|
||||
--label "org.opencontainers.image.created=${CREATED}" \
|
||||
--label "org.opencontainers.image.source=${URL}" \
|
||||
--label "org.opencontainers.image.vendor=${VENDOR}" \
|
||||
--label "org.opencontainers.image.title=${TITLE}" \
|
||||
--label "org.opencontainers.image.description=${DESCRIPTION}" \
|
||||
. || exit $?
|
||||
|
||||
if [ "$PUSH_LATEST" == "1" ]; then
|
||||
podman push "${IMAGE_LATEST}"
|
||||
fi
|
||||
|
1
ui/env.d.ts
vendored
Normal file
1
ui/env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
13
ui/index.html
Normal file
13
ui/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
5046
ui/package-lock.json
generated
Normal file
5046
ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
29
ui/package.json
Normal file
29
ui/package.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "dchat-ui",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"daisyui": "^2.51.5",
|
||||
"vue": "^3.2.47",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.14.2",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.22",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"typescript": "~4.8.4",
|
||||
"vite": "^4.1.4",
|
||||
"vue-tsc": "^1.2.0"
|
||||
}
|
||||
}
|
6
ui/postcss.config.js
Normal file
6
ui/postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
BIN
ui/public/favicon.ico
Normal file
BIN
ui/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
37
ui/src/App.vue
Normal file
37
ui/src/App.vue
Normal file
|
@ -0,0 +1,37 @@
|
|||
<script setup lang="ts">
|
||||
import { RouterView } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<div class="navbar bg-base-100">
|
||||
<div class="flex-1">
|
||||
<a class="btn btn-ghost normal-case text-xl">
|
||||
<RouterLink to="/">dchat</RouterLink>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
<ul class="menu menu-horizontal px-1">
|
||||
<li class="m-1">
|
||||
<RouterLink to="/servers">Servers</RouterLink>
|
||||
</li>
|
||||
<li class="m-1">
|
||||
<RouterLink to="/users">Users</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<Suspense>
|
||||
<RouterView />
|
||||
</Suspense>
|
||||
<footer class="footer footer-center p-4 bg-base-300 text-base-content">
|
||||
<div>
|
||||
<p>Copyright © 2023 Augusto Dwenger J.</p>
|
||||
<p>This website is licensed under the <a href="https://git.hhhammer.de/hamburghammer/dchat/raw/branch/main/LICENSE"
|
||||
class="link">MIT License</a>
|
||||
and uses open source software. View the <a href="https://git.hhhammer.de/hamburghammer/dchat" class="link">source
|
||||
code</a> on Forgejo.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
3
ui/src/assets/base.css
Normal file
3
ui/src/assets/base.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
1
ui/src/assets/main.css
Normal file
1
ui/src/assets/main.css
Normal file
|
@ -0,0 +1 @@
|
|||
@import './base.css';
|
36
ui/src/components/ModalComponent.vue
Normal file
36
ui/src/components/ModalComponent.vue
Normal file
|
@ -0,0 +1,36 @@
|
|||
<script setup lang="ts">
|
||||
export interface Props {
|
||||
modalId: string | number,
|
||||
openModal: { class: string, label: string },
|
||||
isDestructive?: boolean,
|
||||
submitAction: () => Promise<void>,
|
||||
cancelAction?: () => Promise<void>,
|
||||
onLoadAction?: () => Promise<void>
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isDestructive: false,
|
||||
cancelAction: () => Promise.resolve(),
|
||||
onLoadAction: () => Promise.resolve()
|
||||
})
|
||||
const modalId = "modal-" + props.modalId
|
||||
</script>
|
||||
<template>
|
||||
<!-- The button to open modal -->
|
||||
<label v-bind:for="modalId" v-bind:class="props.openModal.class" @click="props.onLoadAction">
|
||||
{{ props.openModal.label }}
|
||||
</label>
|
||||
|
||||
<!-- Put this part before </body> tag -->
|
||||
<input type="checkbox" v-bind:id="modalId" class="modal-toggle" />
|
||||
<div class="modal">
|
||||
<div class="modal-box">
|
||||
<slot />
|
||||
<div class="modal-action">
|
||||
<label v-if="props.isDestructive" v-bind:for="modalId" class="btn btn-error" @click="props.submitAction">Delete</label>
|
||||
<label v-else v-bind:for="modalId" class="btn btn-success" @click="props.submitAction">Save</label>
|
||||
<label v-bind:for="modalId" class="btn" @click="props.cancelAction">Cancel</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
35
ui/src/components/TableComponent.vue
Normal file
35
ui/src/components/TableComponent.vue
Normal file
|
@ -0,0 +1,35 @@
|
|||
<script lang="ts" setup>
|
||||
// index arg being the index number of the row
|
||||
type ActionFunction = (index: number) => void
|
||||
const { tableHeader, tableRows, showActions } = defineProps<{
|
||||
tableHeader: string[],
|
||||
tableRows: any[][],
|
||||
showActions: boolean
|
||||
}>()
|
||||
</script>
|
||||
<template>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table w-full">
|
||||
<!-- head -->
|
||||
<thead>
|
||||
<tr>
|
||||
<template v-for="header in tableHeader">
|
||||
<th>{{ header }}</th>
|
||||
</template>
|
||||
<th v-if="showActions">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- row -->
|
||||
<tr v-for="row in tableRows">
|
||||
<template v-for="data in row">
|
||||
<th>{{ data }}</th>
|
||||
</template>
|
||||
<th>
|
||||
<slot name="action" :row="row" />
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
4
ui/src/components/TestComponent.vue
Normal file
4
ui/src/components/TestComponent.vue
Normal file
|
@ -0,0 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
const {id} = defineProps<{id: any}>()
|
||||
debugger
|
||||
</script>
|
34
ui/src/components/server/AddServerConfig.vue
Normal file
34
ui/src/components/server/AddServerConfig.vue
Normal file
|
@ -0,0 +1,34 @@
|
|||
<script setup lang="ts">
|
||||
import ModalComponent from '@/components/ModalComponent.vue';
|
||||
import type { ServerConfig } from "@/models/server";
|
||||
import { addConfig } from '@/services/ServerConfigs';
|
||||
import { ref } from 'vue'
|
||||
|
||||
const modalId = "addServerConfig"
|
||||
|
||||
const newConfig = ref<ServerConfig | Record<string, never>>({})
|
||||
const submitAction = async () => {
|
||||
if (!newConfig) {
|
||||
return
|
||||
}
|
||||
return addConfig(newConfig.value as ServerConfig)
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<ModalComponent :modalId="modalId" :openModal="{ class: 'btn btn-circle btn-success', label: 'Add' }"
|
||||
:submitAction="submitAction">
|
||||
<h1 class="text-xl normal-case">Add Server Config</h1>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">Server ID</label>
|
||||
<input v-model="newConfig.serverId" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">System Message</label>
|
||||
<textarea v-model="newConfig.systemMessage" class="textarea textarea-bordered w-2/3 h-2/3" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">Rate Limit</label>
|
||||
<input v-model.number="newConfig.rateLimit" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
</ModalComponent>
|
||||
</template>
|
17
ui/src/components/server/DeleteServerConfig.vue
Normal file
17
ui/src/components/server/DeleteServerConfig.vue
Normal file
|
@ -0,0 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import ModalComponent from '@/components/ModalComponent.vue';
|
||||
import { deleteConfig } from '@/services/ServerConfigs';
|
||||
|
||||
const { id } = defineProps<{ id: number }>()
|
||||
|
||||
const submitAction = async () => {
|
||||
return deleteConfig(id)
|
||||
}
|
||||
const modalId = "delete-" + id
|
||||
</script>
|
||||
<template>
|
||||
<ModalComponent :modalId="modalId" :openModal="{ class: 'btn btn-sm btn-error m-1', label: 'Delete' }"
|
||||
:submitAction="submitAction" :isDestructive="true">
|
||||
<h1 class="text-xl normal-case">Are you sure?</h1>
|
||||
</ModalComponent>
|
||||
</template>
|
41
ui/src/components/server/EditServerConfig.vue
Normal file
41
ui/src/components/server/EditServerConfig.vue
Normal file
|
@ -0,0 +1,41 @@
|
|||
<script setup lang="ts">
|
||||
import ModalComponent from '@/components/ModalComponent.vue';
|
||||
import { getConfig } from '@/services/ServerConfigs'
|
||||
import { ref } from 'vue';
|
||||
import type { ServerConfig } from '@/models/server';
|
||||
import { updateConfig } from '@/services/ServerConfigs';
|
||||
|
||||
const { id } = defineProps<{ id: number }>()
|
||||
|
||||
const configToEdit = ref<ServerConfig | Record<string, never>>({})
|
||||
const submitAction = async () => {
|
||||
return updateConfig(id, configToEdit.value as ServerConfig)
|
||||
|
||||
}
|
||||
const onLoadAction = async () => {
|
||||
configToEdit.value = await getConfig(id)
|
||||
}
|
||||
const modalId = "edit-" + id
|
||||
</script>
|
||||
<template>
|
||||
<ModalComponent :modalId="modalId" :openModal="{ class: 'btn btn-sm btn-info m-1', label: 'Edit' }"
|
||||
:submitAction="submitAction" :onLoadAction="onLoadAction">
|
||||
<h1 class="text-xl normal-case">Edit Server Config</h1>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">ID</label>
|
||||
<input v-model.number="configToEdit.id" class="input input-bordered w-full max-w-xs" disabled />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">Server ID</label>
|
||||
<input v-model="configToEdit.serverId" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">System Message</label>
|
||||
<textarea v-model="configToEdit.systemMessage" class="textarea textarea-bordered w-2/3 h-2/3" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">Rate Limit</label>
|
||||
<input v-model.number="configToEdit.rateLimit" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
</ModalComponent>
|
||||
</template>
|
35
ui/src/components/user/AddUserConfig.vue
Normal file
35
ui/src/components/user/AddUserConfig.vue
Normal file
|
@ -0,0 +1,35 @@
|
|||
<script setup lang="ts">
|
||||
import ModalComponent from '@/components/ModalComponent.vue';
|
||||
import type { UserConfig } from "@/models/user";
|
||||
import { ref } from 'vue'
|
||||
import { addConfig } from '@/services/UserConfigs';
|
||||
|
||||
const modalId = "addUserConfig"
|
||||
|
||||
const newConfig = ref<UserConfig | Record<string, never>>({})
|
||||
const submitAction = async () => {
|
||||
return addConfig(newConfig.value as UserConfig)
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<ModalComponent :modalId="modalId" :openModal="{ class: 'btn btn-circle btn-success', label: 'Add' }"
|
||||
:submitAction="submitAction">
|
||||
<h1 class="text-xl normal-case">Add User Config</h1>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">User ID</label>
|
||||
<input v-model="newConfig.userId" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">System Message</label>
|
||||
<textarea v-model="newConfig.systemMessage" class="textarea textarea-bordered w-2/3 h-2/3" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">Context Length</label>
|
||||
<input v-model.number="newConfig.contextLength" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">Rate Limit</label>
|
||||
<input v-model.number="newConfig.rateLimit" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
</ModalComponent>
|
||||
</template>
|
17
ui/src/components/user/DeleteUserConfig.vue
Normal file
17
ui/src/components/user/DeleteUserConfig.vue
Normal file
|
@ -0,0 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import ModalComponent from '@/components/ModalComponent.vue';
|
||||
import { deleteConfig } from '@/services/UserConfigs';
|
||||
|
||||
const { id } = defineProps<{ id: number }>()
|
||||
|
||||
const submitAction = async () => {
|
||||
return deleteConfig(id)
|
||||
}
|
||||
const modalId = "delete-" + id
|
||||
</script>
|
||||
<template>
|
||||
<ModalComponent :modalId="modalId" :openModal="{ class: 'btn btn-sm btn-error m-1', label: 'Delete' }"
|
||||
:submitAction="submitAction" :isDestructive="true">
|
||||
<h1 class="text-xl normal-case">Are you sure?</h1>
|
||||
</ModalComponent>
|
||||
</template>
|
44
ui/src/components/user/EditUserConfig.vue
Normal file
44
ui/src/components/user/EditUserConfig.vue
Normal file
|
@ -0,0 +1,44 @@
|
|||
<script setup lang="ts">
|
||||
import ModalComponent from '@/components/ModalComponent.vue';
|
||||
import { getConfig } from '@/services/UserConfigs'
|
||||
import { ref } from 'vue';
|
||||
import type { UserConfig } from '@/models/user';
|
||||
import { updateConfig } from '@/services/UserConfigs';
|
||||
|
||||
const { id } = defineProps<{ id: number }>()
|
||||
|
||||
const configToEdit = ref<UserConfig | Record<string, never>>({})
|
||||
const submitAction = async () => {
|
||||
return updateConfig(id, configToEdit.value as UserConfig)
|
||||
}
|
||||
const onLoadAction = async () => {
|
||||
configToEdit.value = await getConfig(id)
|
||||
}
|
||||
const modalId = "edit-" + id
|
||||
</script>
|
||||
<template>
|
||||
<ModalComponent :modalId="modalId" :openModal="{ class: 'btn btn-sm btn-info m-1', label: 'Edit' }"
|
||||
:submitAction="submitAction" :onLoadAction="onLoadAction">
|
||||
<h1 class="text-xl normal-case">Edit User Config</h1>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">ID</label>
|
||||
<input v-model.number="configToEdit.id" class="input input-bordered w-full max-w-xs" disabled />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">User ID</label>
|
||||
<input v-model="configToEdit.userId" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">System Message</label>
|
||||
<textarea v-model="configToEdit.systemMessage" class="textarea textarea-bordered w-2/3 h-2/3" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">Context Length</label>
|
||||
<input v-model.number="configToEdit.contextLength" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<label class="mr-4">Rate Limit</label>
|
||||
<input v-model.number="configToEdit.rateLimit" class="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
</ModalComponent>
|
||||
</template>
|
11
ui/src/main.ts
Normal file
11
ui/src/main.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from '@/App.vue'
|
||||
import router from './router'
|
||||
|
||||
import './assets/main.css'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
8
ui/src/models/server.ts
Normal file
8
ui/src/models/server.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
export type ServerConfig = {
|
||||
id: number,
|
||||
serverId: string,
|
||||
systemMessage: string,
|
||||
rateLimit: number,
|
||||
time: string
|
||||
}
|
||||
|
8
ui/src/models/user.ts
Normal file
8
ui/src/models/user.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
export type UserConfig = {
|
||||
id: number,
|
||||
userId: string,
|
||||
systemMessage: string,
|
||||
contextLength: number,
|
||||
rateLimit: number,
|
||||
time: string
|
||||
}
|
39
ui/src/router/index.ts
Normal file
39
ui/src/router/index.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '@/views/HomeView.vue'
|
||||
import IndexServersView from '@/views/servers/IndexServersView.vue'
|
||||
import ServerConfigsView from '@/views/servers/ServerConfigsView.vue'
|
||||
import IndexUsersView from '@/views/users/IndexUsersView.vue'
|
||||
import UserConfigsView from '@/views/users/UserConfigsView.vue'
|
||||
|
||||
const routerConfig = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/servers',
|
||||
name: 'servers',
|
||||
component: IndexServersView
|
||||
},
|
||||
{
|
||||
path: '/servers/configs',
|
||||
name: 'serverConfigs',
|
||||
component: ServerConfigsView
|
||||
},
|
||||
{
|
||||
path: '/users',
|
||||
name: 'users',
|
||||
component: IndexUsersView
|
||||
},
|
||||
{
|
||||
path: '/users/configs',
|
||||
name: 'userConfigs',
|
||||
component: UserConfigsView
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default routerConfig
|
39
ui/src/services/ServerConfigs.ts
Normal file
39
ui/src/services/ServerConfigs.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import type { ServerConfig } from "@/models/server";
|
||||
|
||||
const configUrl = "http://localhost:7070/api/servers/configs/"
|
||||
|
||||
export async function getConfigs(): Promise<ServerConfig[]> {
|
||||
return fetch(configUrl)
|
||||
.then(shouldBeSuccess)
|
||||
.then(res => res.json())
|
||||
}
|
||||
|
||||
export function getConfig(id: number): Promise<ServerConfig> {
|
||||
return fetch(configUrl + id)
|
||||
.then(shouldBeSuccess)
|
||||
.then(res => res.json())
|
||||
}
|
||||
|
||||
export function updateConfig(id: number, config: ServerConfig): Promise<void> {
|
||||
return fetch(configUrl + id, { method: "PATCH", body: JSON.stringify(config) })
|
||||
.then(shouldBeSuccess)
|
||||
.then()
|
||||
}
|
||||
|
||||
export function addConfig(config: ServerConfig): Promise<void> {
|
||||
debugger
|
||||
return fetch(configUrl, { method: "POST", body: JSON.stringify(config) })
|
||||
.then(shouldBeSuccess)
|
||||
.then()
|
||||
}
|
||||
|
||||
export function deleteConfig(id: number): Promise<void> {
|
||||
return fetch(configUrl + id, { method: "DELETE" })
|
||||
.then(shouldBeSuccess)
|
||||
.then()
|
||||
}
|
||||
|
||||
function shouldBeSuccess(res: Response): Response {
|
||||
if (res.status >= 200 && res.status < 300) return res
|
||||
throw new Error(`Expected success status code but got: ${res.status} ${res.statusText}`)
|
||||
}
|
40
ui/src/services/UserConfigs.ts
Normal file
40
ui/src/services/UserConfigs.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import type { UserConfig } from '@/models/user'
|
||||
|
||||
const configUrl = "http://localhost:7070/api/users/configs/"
|
||||
|
||||
export async function getConfigs(): Promise<UserConfig[]> {
|
||||
return fetch(configUrl)
|
||||
.then(shouldBeSuccess)
|
||||
.then(res => res.json())
|
||||
}
|
||||
|
||||
export function getConfig(id: number): Promise<UserConfig> {
|
||||
return fetch(configUrl + id)
|
||||
.then(shouldBeSuccess)
|
||||
.then(res => res.json())
|
||||
}
|
||||
|
||||
export function updateConfig(id: number, config: UserConfig): Promise<void> {
|
||||
return fetch(configUrl + id, { method: "PATCH", body: JSON.stringify(config) })
|
||||
.then(shouldBeSuccess)
|
||||
.then()
|
||||
}
|
||||
|
||||
export function addConfig(config: UserConfig): Promise<void> {
|
||||
debugger
|
||||
return fetch(configUrl, { method: "POST", body: JSON.stringify(config) })
|
||||
.then(shouldBeSuccess)
|
||||
.then()
|
||||
}
|
||||
|
||||
export function deleteConfig(id: number): Promise<void> {
|
||||
return fetch(configUrl + id, { method: "DELETE" })
|
||||
.then(shouldBeSuccess)
|
||||
.then()
|
||||
}
|
||||
|
||||
|
||||
function shouldBeSuccess(res: Response): Response {
|
||||
if (res.status >= 200 && res.status < 300) return res
|
||||
throw new Error(`Expected success status code but got: ${res.status} ${res.statusText}`)
|
||||
}
|
13
ui/src/views/HomeView.vue
Normal file
13
ui/src/views/HomeView.vue
Normal file
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<h1 class="text-xl normal-case m-5">Overview</h1>
|
||||
<div class="flex justify-center m-4">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<RouterLink to="/servers">
|
||||
<button class="btn btn-wide btn-outline">Servers</button>
|
||||
</RouterLink>
|
||||
<RouterLink to="/users">
|
||||
<button class="btn btn-wide btn-outline">Users</button>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
5
ui/src/views/UsersView.vue
Normal file
5
ui/src/views/UsersView.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
</script>
|
||||
<template>
|
||||
|
||||
</template>
|
12
ui/src/views/servers/IndexServersView.vue
Normal file
12
ui/src/views/servers/IndexServersView.vue
Normal file
|
@ -0,0 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
</script>
|
||||
<template>
|
||||
<h1 class="text-xl normal-case m-5">Server overview</h1>
|
||||
<div class="flex justify-center m-4">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<RouterLink to="/servers/configs">
|
||||
<button class="btn btn-wide btn-outline">Configs</button>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
27
ui/src/views/servers/ServerConfigsView.vue
Normal file
27
ui/src/views/servers/ServerConfigsView.vue
Normal file
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import TableComponent from '@/components/TableComponent.vue';
|
||||
import AddServerConfig from '@/components/server/AddServerConfig.vue';
|
||||
import { getConfigs } from '@/services/ServerConfigs'
|
||||
import EditServerConfig from '@/components/server/EditServerConfig.vue';
|
||||
import DeleteServerConfig from '@/components/server/DeleteServerConfig.vue';
|
||||
|
||||
const trimSystemMessage = (str: string) => str.length > 50 ? str.slice(0, 47) + "..." : str
|
||||
|
||||
const serverConfigs = await getConfigs()
|
||||
|
||||
const tableHeader = ["ID", "Server ID", "System Message", "Rate limit", "Created"]
|
||||
const tableRows = serverConfigs.map(config => [config.id, config.serverId, trimSystemMessage(config.systemMessage), config.rateLimit, config.time])
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex justify-between m-5">
|
||||
<h1 class="text-xl normal-case">Server Configs</h1>
|
||||
<AddServerConfig />
|
||||
</div>
|
||||
<TableComponent v-if="serverConfigs.length" :tableHeader="tableHeader" :tableRows="tableRows" :showActions="true">
|
||||
<template #action="action">
|
||||
<EditServerConfig :id="action.row[0]" />
|
||||
<DeleteServerConfig :id="action.row[0]" />
|
||||
</template>
|
||||
</TableComponent>
|
||||
<p v-else class="m-5">No server configs</p>
|
||||
</template>
|
12
ui/src/views/users/IndexUsersView.vue
Normal file
12
ui/src/views/users/IndexUsersView.vue
Normal file
|
@ -0,0 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
</script>
|
||||
<template>
|
||||
<h1 class="text-xl normal-case m-5">Server overview</h1>
|
||||
<div class="flex justify-center m-4">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<RouterLink to="/users/configs">
|
||||
<button class="btn btn-wide btn-outline">Configs</button>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
27
ui/src/views/users/UserConfigsView.vue
Normal file
27
ui/src/views/users/UserConfigsView.vue
Normal file
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import TableComponent from '@/components/TableComponent.vue';
|
||||
import AddUserConfig from '@/components/user/AddUserConfig.vue';
|
||||
import { getConfigs } from '@/services/UserConfigs'
|
||||
import EditUserConfig from '@/components/user/EditUserConfig.vue';
|
||||
import DeleteServerConfig from '@/components/server/DeleteServerConfig.vue';
|
||||
|
||||
const trimSystemMessage = (str: string) => str.length > 50 ? str.slice(0, 47) + "..." : str
|
||||
|
||||
const userConfigs = await getConfigs()
|
||||
|
||||
const tableHeader = ["ID", "User ID", "System Message", "Context Length", "Rate limit", "Created"]
|
||||
const tableRows = userConfigs.map(config => [config.id, config.userId, trimSystemMessage(config.systemMessage), config.contextLength, config.rateLimit, config.time])
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex justify-between m-5">
|
||||
<h1 class="text-xl normal-case">User Configs</h1>
|
||||
<AddUserConfig />
|
||||
</div>
|
||||
<TableComponent v-if="userConfigs.length" :tableHeader="tableHeader" :tableRows="tableRows" :showActions="true">
|
||||
<template #action="action">
|
||||
<EditUserConfig :id="action.row[0]" />
|
||||
<DeleteServerConfig :id="action.row[0]" />
|
||||
</template>
|
||||
</TableComponent>
|
||||
<p v-else class="m-5">No user configs</p>
|
||||
</template>
|
9
ui/tailwind.config.js
Normal file
9
ui/tailwind.config.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require("daisyui")],
|
||||
}
|
||||
|
16
ui/tsconfig.json
Normal file
16
ui/tsconfig.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
8
ui/tsconfig.node.json
Normal file
8
ui/tsconfig.node.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.node.json",
|
||||
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
14
ui/vite.config.ts
Normal file
14
ui/vite.config.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
Loading…
Reference in a new issue