diff --git a/.github/workflows/build-cache-server.yml b/.github/workflows/build-cache-server.yml new file mode 100644 index 0000000..2317a65 --- /dev/null +++ b/.github/workflows/build-cache-server.yml @@ -0,0 +1,53 @@ +name: "Build CosmicKube cache server" +on: + push: + branches: + - main +jobs: + build-server: + name: "Build Cache Server" + runs-on: ubuntu-20.04 + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download Go + uses: actions/setup-go@v5 + with: + go-version: '^1.22' + + - name: Build + run: | + cd kube_cache + go build && strip kube_cache + + - name: Upload Artifact + uses: actions/upload-artifact@v1 + with: + name: cache_server + path: kube_cache/kube_cache + + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # this will push the docker images to the github container registry + # you will need to give actions permission to push there first + - name: Build and push + id: docker_build + uses: docker/build-push-action@v3 + with: + context: . + file: kube_cache/cache.Dockerfile + push: true + tags: | + ghcr.io/pilksoc/kubecache:latest + ghcr.io/pilksoc/kubecache:dev-${{ github.run_number }} \ No newline at end of file diff --git a/.github/workflows/build-game-client.yml b/.github/workflows/build-game-client.yml index 07f8457..f9d9221 100644 --- a/.github/workflows/build-game-client.yml +++ b/.github/workflows/build-game-client.yml @@ -2,7 +2,7 @@ name: "Build CosmicKube game client" on: push: branches: - - main + - main env: GODOT_VERSION: 4.2.1 @@ -59,4 +59,4 @@ jobs: ###### Repository/Build Configurations ###### app_location: "build/web" skip_app_build: true - ###### End of Repository/Build Configurations ###### \ No newline at end of file + ###### End of Repository/Build Configurations ###### diff --git a/.github/workflows/build-game-server.yml b/.github/workflows/build-game-server.yml index d97d216..af3dfbf 100644 --- a/.github/workflows/build-game-server.yml +++ b/.github/workflows/build-game-server.yml @@ -48,7 +48,7 @@ jobs: uses: docker/build-push-action@v3 with: context: . - file: server.Dockerfile + file: backend/server.Dockerfile push: true tags: | ghcr.io/pilksoc/cosmickube:dev-latest diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 1096e14..d2d3c54 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -21,3 +21,4 @@ futures = { version = "0.3", default-features=false} serde_repr = "0.1" reqwest = "0.11.24" url = "2.5.0" +rand = "0.8.5" diff --git a/server.Dockerfile b/backend/server.Dockerfile similarity index 60% rename from server.Dockerfile rename to backend/server.Dockerfile index 4d39d78..7443051 100644 --- a/server.Dockerfile +++ b/backend/server.Dockerfile @@ -1,6 +1,6 @@ -FROM alpine:3.19.1 +FROM debian:12.5-slim -RUN apk update && apk add ca-certificates && apk cache clean +RUN apt update && apt install ca-certificates -y && apt upgrade -y WORKDIR /usr/local/cosmic_kube COPY backend/target/release/cosmic_kube_amd64 /usr/local/bin/cosmic_kube diff --git a/backend/src/handlers.rs b/backend/src/handlers.rs index c2a2eb3..c8a2e64 100644 --- a/backend/src/handlers.rs +++ b/backend/src/handlers.rs @@ -1,9 +1,15 @@ use crate::{ws, Clients, Result}; use warp::Reply; -pub async fn ws_handler(ws: warp::ws::Ws, clients: Clients) -> Result -{ +pub async fn ws_handler(ws: warp::ws::Ws, clients: Clients) -> Result { println!("ws_handler"); //debug Ok(ws.on_upgrade(move |socket| ws::client_connection(socket, clients))) -} \ No newline at end of file +} + +pub async fn metrics_handler(clients: Clients) -> Result { + let metricsString = "cosmic_kube_clients{type=\"connected\"} ".to_string() + + &clients.lock().await.len().to_string() + + "\n"; + Ok(metricsString) +} diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 1dc5894..1a6487f 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -7,4 +7,4 @@ pub mod local_grid; pub mod cache_client; pub mod recipe; -type Coordinate = [u64; 2]; +pub type Coordinate = [u64; 2]; diff --git a/backend/src/main.rs b/backend/src/main.rs index d2743b4..5033bfa 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -18,22 +18,23 @@ type Result = std::result::Result; #[tokio::main] async fn main() { - //initialise a hashmap to store currently connected clients. We may want some more logic here if we want currently connected clients to be stored somewhere let clients: Clients = Arc::new(Mutex::new(HashMap::new())); - println!("configuring websocket route"); //debug + println!("Configuring websocket route"); //debug let ws_route = warp::path("ws") - .and(warp::ws()) - .and(with_clients(clients.clone())) - .and_then(handlers::ws_handler); + .and(warp::ws()) + .and(with_clients(clients.clone())) + .and_then(handlers::ws_handler) + .or(warp::path("metrics") + .and(with_clients(clients.clone())) + .and_then(handlers::metrics_handler)); let routes = ws_route.with(warp::cors().allow_any_origin()); - println!("starting server"); //debug - warp::serve(routes).run(([127, 0, 0, 1], 8000)).await; //PORT 8000 localhost + println!("Starting server on http://0.0.0.0:8000"); //debug + warp::serve(routes).run(([0, 0, 0, 0], 8000)).await; } fn with_clients(clients: Clients) -> impl Filter + Clone { warp::any().map(move || clients.clone()) } - diff --git a/backend/src/websocketstructs.rs b/backend/src/websocketstructs.rs deleted file mode 100644 index 8b13789..0000000 --- a/backend/src/websocketstructs.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/backend/src/ws/gen_json.rs b/backend/src/ws/gen_json.rs index 3ef19b7..e43e7cb 100644 --- a/backend/src/ws/gen_json.rs +++ b/backend/src/ws/gen_json.rs @@ -1,14 +1,24 @@ -use core::fmt; +use cosmic_kube::grid::Grid; use cosmic_kube::kube::Kube; +use cosmic_kube::local_grid::LocalGrid; +use cosmic_kube::space::{Space, SpaceKind}; +use cosmic_kube::Coordinate; +use core::fmt; +use rand::Rng; use serde::{Deserialize, Serialize}; -use serde_json::json; -use serde_repr::{Serialize_repr, Deserialize_repr}; +use serde_json::{json, Value}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +//example valid json: +// { "initialised": true, "player": "charlie zoot", "coordinates": [10, 10], "action": { "kind": 1, "kube": { "id": {"uuid": "f7993723-2529-50c4-950d-ba104d29b5df" }, "name": "dirt" }, "coordinates": [10,11] } } + // this is the data we expect to recieve from the player #[derive(Serialize, Deserialize)] pub struct PlayerInfo { - player: String, //Player, //the player requesting the data - coordinates: [u64; 2], //current player coordinates + initialised: bool, + player: String, //Player, //the player requesting the data + coordinates: [u64; 2], //current player coordinates action: Option, // 0, block picked up 1, block placed } @@ -32,30 +42,72 @@ impl fmt::Display for ActionType { pub struct Action { kind: ActionType, kube: Kube, + coordinates: Coordinate, } -fn recalculate_game(state: PlayerInfo) -> String { +fn perform_action(mut grid: Grid, action: Action) -> Grid { + let kube_result: SpaceKind; + match action.kind { + ActionType::Pickup => kube_result = SpaceKind::EmptySpace, + ActionType::Place => kube_result = SpaceKind::Kube(action.kube), + } + + let space_in_question: Space = Space::new(action.coordinates, kube_result); + grid.insert(space_in_question); + grid +} + +fn debug_message(state: &PlayerInfo) { // debug: log of event to server console - println!("{} @ (x:{}, y:{})", state.player, state.coordinates[0], state.coordinates[1]); - match state.action { + println!( + "{} @ (x:{}, y:{})", + state.player, state.coordinates[0], state.coordinates[1] + ); + let mut _has_action: bool = true; + match &state.action { Some(p) => println!("{}: {}", p.kind, p.kube.name), - None => println!(""), + None => _has_action = false, } +} - //send action to database to get result !!!<---- +fn recalculate_game(state: PlayerInfo) -> String { + debug_message(&state); //debug + + // total_grid: Grid = get grid from GO db + let example_grid: Grid = Grid::new(2048, 2048); + let new_grid_to_send: Grid; + + // then we want to update the grid by performing action + match state.action { + Some(p) => new_grid_to_send = perform_action(example_grid, p), + _ => new_grid_to_send = example_grid, + } - let resp = json!({ - "grid" : "edited grid" - }); + //store new_grid_to_send in the database - resp.to_string() + let new_grid: LocalGrid = + LocalGrid::from_grid_and_coord(&new_grid_to_send, state.coordinates, 48); + let resp: Value; + + if state.initialised { + // if the player is not new to the game, continue game loop + resp = json!({ + "grid" : new_grid, + }); + } else { + let mut rng = rand::thread_rng(); + resp = json!({ + "coordinates" : [rng.gen_range(0..2048), rng.gen_range(0..2048)] + }); + } + + resp.to_string() } pub fn create_response(message: &str) -> String { - // Parse the string of data into serde_json::Value. - let state: PlayerInfo = serde_json::from_str(message).expect("something went wrong in json parse"); + let state: PlayerInfo = + serde_json::from_str(message).expect("something went wrong in json parse"); recalculate_game(state) } - diff --git a/diesel.toml b/diesel.toml deleted file mode 100644 index be61e3e..0000000 --- a/diesel.toml +++ /dev/null @@ -1,9 +0,0 @@ -# For documentation on how to configure this file, -# see https://diesel.rs/guides/configuring-diesel-cli - -[print_schema] -file = "backend/src/schema.rs" -custom_type_derives = ["diesel::query_builder::QueryId"] - -[migrations_directory] -dir = "migrations" diff --git a/game-source/WebSockets.gd b/game-source/WebSockets.gd new file mode 100644 index 0000000..9090089 --- /dev/null +++ b/game-source/WebSockets.gd @@ -0,0 +1,79 @@ +extends Node +class_name WebSocketClient + +@export var handshake_headers : PackedStringArray +@export var supported_protocols : PackedStringArray +@export var tls_trusted_certificate : X509Certificate +@export var tls_verify := true + + +var socket = WebSocketPeer.new() +var last_state = WebSocketPeer.STATE_CLOSED + + +signal connected_to_server() +signal connection_closed() +signal message_received(message: Variant) + + +func connect_to_url(url) -> int: + socket.supported_protocols = supported_protocols + socket.handshake_headers = handshake_headers + var tls_options = TLSOptions.client(tls_trusted_certificate) + var err = socket.connect_to_url(url, tls_options) + if err != OK: + print("i tried :(", err) + return err + last_state = socket.get_ready_state() + return OK + + +func send(message) -> int: + print(socket.get_ready_state()) + if typeof(message) == TYPE_STRING: + return socket.send_text(message) + return socket.send(var_to_bytes(message)) + + +func get_message() -> Variant: + if socket.get_available_packet_count() < 1: + return null + var pkt = socket.get_packet() + if socket.was_string_packet(): + return pkt.get_string_from_utf8() + return bytes_to_var(pkt) + + +func close(code := 1000, reason := "") -> void: + socket.close(code, reason) + last_state = socket.get_ready_state() + + +func clear() -> void: + socket = WebSocketPeer.new() + last_state = socket.get_ready_state() + + +func get_socket() -> WebSocketPeer: + return socket + + +func poll() -> void: + if socket.get_ready_state() != socket.STATE_CLOSED: + socket.poll() + var state = socket.get_ready_state() + if last_state != state: + last_state = state + if state == socket.STATE_OPEN: + connected_to_server.emit() + elif state == socket.STATE_CLOSED: + connection_closed.emit() + while socket.get_ready_state() == socket.STATE_OPEN and socket.get_available_packet_count(): + message_received.emit(get_message()) + + +func _ready(): + connect_to_url("ws://localhost:8000/ws") + +func _process(delta): + poll() diff --git a/game-source/assets/Illustration95.png b/game-source/assets/Illustration95.png new file mode 100644 index 0000000..8a60264 Binary files /dev/null and b/game-source/assets/Illustration95.png differ diff --git a/game-source/icon.svg.import b/game-source/assets/Illustration95.png.import similarity index 62% rename from game-source/icon.svg.import rename to game-source/assets/Illustration95.png.import index 672e88c..c54a4ec 100644 --- a/game-source/icon.svg.import +++ b/game-source/assets/Illustration95.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bay85nk58yj5e" -path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +uid="uid://bdap4f6w37byq" +path="res://.godot/imported/Illustration95.png-83b3dbf36a7985d78f181801efd673ad.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://icon.svg" -dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] +source_file="res://assets/Illustration95.png" +dest_files=["res://.godot/imported/Illustration95.png-83b3dbf36a7985d78f181801efd673ad.ctex"] [params] @@ -32,6 +32,3 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/client.Dockerfile b/game-source/client.Dockerfile similarity index 100% rename from client.Dockerfile rename to game-source/client.Dockerfile diff --git a/game-source/icon.svg b/game-source/icon.svg deleted file mode 100644 index b370ceb..0000000 --- a/game-source/icon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/game-source/main.tscn b/game-source/main.tscn deleted file mode 100644 index 5761a76..0000000 --- a/game-source/main.tscn +++ /dev/null @@ -1,22 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://2t2nbl4hrw8b"] - -[ext_resource type="PackedScene" uid="uid://xqoiyxrn10js" path="res://player_character.tscn" id="1_j2rm2"] -[ext_resource type="PackedScene" uid="uid://cv6rvnsd8jc4u" path="res://box.tscn" id="2_n627q"] - -[node name="GameLevel" type="Node2D"] - -[node name="PlayerCharacter" parent="." instance=ExtResource("1_j2rm2")] -position = Vector2(607, 323) -move_speed = 1000.0 - -[node name="box" parent="." instance=ExtResource("2_n627q")] -position = Vector2(986, 524) - -[node name="box2" parent="." instance=ExtResource("2_n627q")] -position = Vector2(140, -16) - -[node name="box3" parent="." instance=ExtResource("2_n627q")] -position = Vector2(189, 538) - -[node name="box4" parent="." instance=ExtResource("2_n627q")] -position = Vector2(1120, -33) diff --git a/game-source/player_character.gd b/game-source/player_character.gd deleted file mode 100644 index 2bff2fd..0000000 --- a/game-source/player_character.gd +++ /dev/null @@ -1,15 +0,0 @@ -extends CharacterBody2D - - -@export var move_speed: float = 400 - - -func _physics_process(delta): - var input_dir = Vector2( - Input.get_action_strength("right") - Input. get_action_strength("left"), - Input.get_action_strength("down") - Input.get_action_strength("up") - ) - - velocity = input_dir*move_speed - - move_and_slide() diff --git a/game-source/player_character.tscn b/game-source/player_character.tscn deleted file mode 100644 index eadb214..0000000 --- a/game-source/player_character.tscn +++ /dev/null @@ -1,20 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://xqoiyxrn10js"] - -[ext_resource type="Texture2D" uid="uid://cf7mjmudnjchn" path="res://assets/lil_guy.png" id="1_rfqfo"] -[ext_resource type="Script" path="res://player_character.gd" id="1_rmbqt"] - -[node name="PlayerCharacter" type="CharacterBody2D"] -script = ExtResource("1_rmbqt") - -[node name="Sprite2D" type="Sprite2D" parent="."] -position = Vector2(-0.5, 3) -scale = Vector2(1.002, 1.008) -texture = ExtResource("1_rfqfo") - -[node name="Camera2D" type="Camera2D" parent="."] - -[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."] -visible = false -position = Vector2(-0.5, 3) -scale = Vector2(1.002, 1.008) -polygon = PackedVector2Array(52, -166.9, 52, -165.3, 63, -160.3, 63, -158.7, 71, -148.7, 71, -145.4, 74, -137.4, 74, -120, 77, -96.1, 77, -78, 82, 49, 80.9, 49, 78.9, 124, 77.5, 124, 74.6, 136, 73, 136, 68, 143, 65.4, 143, 47.4, 150, 40, 150, -60, 149, -60, 147.8, -72, 146.8, -72, 144.7, -79, 136.8, -79, 132.4, -83, 123.4, -83, 121, -80, 56, -80, -49, -80.8, -49, -84, -84.9, -84, -90, -82, -139, -80.5, -139, -78.6, -147, -76.9, -147, -62.8, -160, -60.4, -160, -49.4, -165, -39.4, -165, -26, -167.7, -26, -169.1) diff --git a/game-source/project.godot b/game-source/project.godot index d939711..ea3ff37 100644 --- a/game-source/project.godot +++ b/game-source/project.godot @@ -11,9 +11,16 @@ config_version=5 [application] config/name="Cosmic Kube" -run/main_scene="res://main.tscn" +run/main_scene="res://scenes/main/main.tscn" config/features=PackedStringArray("4.2", "GL Compatibility") -config/icon="res://icon.svg" + +[autoload] + +WebSockets="*res://WebSockets.gd" + +[display] + +window/stretch/mode="canvas_items" [input] diff --git a/game-source/box.tscn b/game-source/scenes/box/box.tscn similarity index 93% rename from game-source/box.tscn rename to game-source/scenes/box/box.tscn index c27965b..1354980 100644 --- a/game-source/box.tscn +++ b/game-source/scenes/box/box.tscn @@ -1,12 +1,12 @@ [gd_scene load_steps=2 format=3 uid="uid://cv6rvnsd8jc4u"] -[ext_resource type="Texture2D" uid="uid://c6jm42o02gsfr" path="res://assets/box.png" id="1_q4dmh"] +[ext_resource type="Texture2D" uid="uid://c6jm42o02gsfr" path="res://assets/box.png" id="1_blfj8"] [node name="CharacterBody2D" type="CharacterBody2D"] [node name="Box" type="Sprite2D" parent="."] position = Vector2(33, 52) -texture = ExtResource("1_q4dmh") +texture = ExtResource("1_blfj8") [node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."] visible = false diff --git a/game-source/scenes/main/main.gd b/game-source/scenes/main/main.gd new file mode 100644 index 0000000..093d424 --- /dev/null +++ b/game-source/scenes/main/main.gd @@ -0,0 +1,21 @@ +extends Node2D +@export var on = true +@export var tile_size = 100 + +func _process(delta): + queue_redraw() + +func _draw(): + if !on: + pass + + var camera: CharacterBody2D = get_tree().current_scene.find_child('PlayerCharacter') + + var size = get_viewport_rect().size / 2 + var cam = camera.position + + for i in range(int((cam.x - size.x) / tile_size) - 1, int((size.x + cam.x) / tile_size) + 1): + draw_line(Vector2(i * tile_size, cam.y + size.y + 100), Vector2(i * tile_size, cam.y - size.y - 100), "000000") + for i in range(int((cam.y - size.y) / tile_size) - 1, int((size.y + cam.y) / tile_size) + 1): + draw_line(Vector2(cam.x + size.x + 100, i * tile_size), Vector2(cam.x - size.x - 100, i * tile_size), "000000") + diff --git a/game-source/scenes/main/main.tscn b/game-source/scenes/main/main.tscn new file mode 100644 index 0000000..a3372c6 --- /dev/null +++ b/game-source/scenes/main/main.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=3 format=3 uid="uid://2t2nbl4hrw8b"] + +[ext_resource type="PackedScene" uid="uid://xqoiyxrn10js" path="res://scenes/player_character/player_character.tscn" id="1_1c02b"] +[ext_resource type="Script" path="res://scenes/main/main.gd" id="1_47q2m"] + +[node name="GameLevel" type="Node2D"] +script = ExtResource("1_47q2m") + +[node name="PlayerCharacter" parent="." instance=ExtResource("1_1c02b")] +position = Vector2(577, 325) +scale = Vector2(2.9, 2.9) diff --git a/game-source/scenes/player_character/player_character.gd b/game-source/scenes/player_character/player_character.gd new file mode 100644 index 0000000..0aec7e5 --- /dev/null +++ b/game-source/scenes/player_character/player_character.gd @@ -0,0 +1,30 @@ +extends CharacterBody2D + + +@export var tile_size: float = 100 +@export var is_moving: bool = false + +var inputs = { + "right": Vector2.RIGHT, + "left": Vector2.LEFT, + "up": Vector2.UP, + "down": Vector2.DOWN +} + +var inputs_rev = { + Vector2.RIGHT:"right", + Vector2.LEFT: "left", + Vector2.UP: "up", + Vector2.DOWN: "down" +} + +func _ready(): + position = position.snapped(Vector2.ONE * tile_size) + position += Vector2.ONE * tile_size / 2 + +func _input(event): + var vec = Input.get_vector("left", "right", "up", "down") + if vec.length() == 1: + position += vec*100 + $AnimationPlayer.play(inputs_rev[vec]) + diff --git a/game-source/scenes/player_character/player_character.tscn b/game-source/scenes/player_character/player_character.tscn new file mode 100644 index 0000000..6406c42 --- /dev/null +++ b/game-source/scenes/player_character/player_character.tscn @@ -0,0 +1,93 @@ +[gd_scene load_steps=8 format=3 uid="uid://xqoiyxrn10js"] + +[ext_resource type="Script" path="res://scenes/player_character/player_character.gd" id="1_rmbqt"] +[ext_resource type="Texture2D" uid="uid://bdap4f6w37byq" path="res://assets/Illustration95.png" id="2_0gsa3"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_nrvy8"] +size = Vector2(14, 15.5) + +[sub_resource type="Animation" id="Animation_17yri"] +resource_name = "walk" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("../Sprite2D:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0.00177135, 0.297301, 0.529481, 0.858887), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 1, +"values": [0, 1, 2, 0] +} + +[sub_resource type="Animation" id="Animation_sndo4"] +resource_name = "up" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("../Sprite2D:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0.00553417, 0.15295, 0.32358), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [3, 4, 5] +} + +[sub_resource type="Animation" id="Animation_v72g0"] +resource_name = "left" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("../Sprite2D:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0.0161553, 0.319068, 0.69064), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [6, 7, 8] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_hbknw"] +_data = { +"down": SubResource("Animation_17yri"), +"right": SubResource("Animation_v72g0"), +"up": SubResource("Animation_sndo4") +} + +[node name="PlayerCharacter" type="CharacterBody2D"] +script = ExtResource("1_rmbqt") + +[node name="RayCast2D" type="RayCast2D" parent="."] +scale = Vector2(1, 1) +target_position = Vector2(0, 19) + +[node name="Sprite2D" type="Sprite2D" parent="."] +scale = Vector2(0.458, 0.368) +texture = ExtResource("2_0gsa3") +hframes = 3 +vframes = 3 + +[node name="Camera2D" type="Camera2D" parent="."] +position_smoothing_enabled = true +position_smoothing_speed = 10.0 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(0, 2.38419e-07) +scale = Vector2(-0.811343, 1.12881) +shape = SubResource("RectangleShape2D_nrvy8") + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +root_node = NodePath("../RayCast2D") +libraries = { +"": SubResource("AnimationLibrary_hbknw") +} + +[node name="AnimationPlayer" type="AnimationPlayer" parent="AnimationPlayer"] +root_node = NodePath("../RayCast2D") +libraries = { +"": SubResource("AnimationLibrary_hbknw") +} diff --git a/kube_cache/.gitignore b/kube_cache/.gitignore index 49fb59c..eeeb870 100644 --- a/kube_cache/.gitignore +++ b/kube_cache/.gitignore @@ -1,2 +1,4 @@ kube_cache .env +*.jpg +*.png diff --git a/kube_cache/README.md b/kube_cache/README.md new file mode 100644 index 0000000..18186ba --- /dev/null +++ b/kube_cache/README.md @@ -0,0 +1,16 @@ +# dotenv setup + +```dotenv +PGHOST=don't put secrets in main +PGUSER=don't put secrets in main +PGPORT=5432 +PGDATABASE=don't put secrets in main +PGPASSWORD=don't put secrets in main + +DATABASE_URL=$PGUSER:$PGPASSWORD@tcp($PGHOST:$PGPORT)/$PGDATABASE?sslmode=require +DATABASE_URL=host=$PGHOST user=$PGUSER password=$PGPASSWORD dbname=$PGDATABASE port=$PGPORT sslmode=require TimeZone=Europe/London + +OPENAI_KEY=don't put secrets in main +OPENAI_ENDPOINT=https://don't put secrets in main.openai.azure.com/ +OPENAI_MODEL_ID=GPTthreeeee +``` diff --git a/kube_cache/aiStuff/ai.go b/kube_cache/aiStuff/ai.go new file mode 100644 index 0000000..27bdcfe --- /dev/null +++ b/kube_cache/aiStuff/ai.go @@ -0,0 +1,187 @@ +package aiStuff + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "sync" + "time" + + "github.com/CosmicKube/kube_cache/metrics" +) + +type KubeAi struct { + Endpoint, Apikey, ModelId string + Metrics *metrics.Metrics + LastAccess time.Time + // Apparently you are only allowed to make one request at a time + Lock sync.Mutex +} + +const apiRestTime = time.Second * 7 + +func New(metrics *metrics.Metrics, endpoint, apiKey, modelId string) *KubeAi { + return &KubeAi{ + Metrics: metrics, + Endpoint: endpoint, + Apikey: apiKey, + ModelId: modelId, + } +} + +const baseRequest = `You are a game master for crafting game. +You will be given an input in the form of multiple elements like; ["object1", "object2", ...] +Your response will be limited to a single element that is formed from the preceding element in the form of a JSON string like; {"name":"object3"} +You can be creative with your responses but it has to somewhat grounded. +If you can't figure one output, say {"name": "I dunno lmao"}. +Only generate the output for the input provided. +Separate the input from the output with a space.` + +func (ai *KubeAi) generateAiPrompt(kubeName1, kubeName2 string) string { + arr := []string{kubeName1, kubeName2} + req, err := json.Marshal(arr) + if err != nil { + log.Printf("Error marshalling kube names: %s", err) + return fmt.Sprintf("[\"%s\", \"%s\"]", kubeName1, kubeName2) + } + + return string(req) +} + +type aiResp struct { + Name string `json:"name"` +} + +type message struct { + Content string `json:"content"` +} + +type choice struct { + Message message `json:"message"` +} + +type openaiResp struct { + Choices []choice `json:"choices"` +} + +type aiMessage struct { + Role string `json:"role"` + Content string `json:"content"` +} + +const ( + topP = 0.95 + maxTokens = 800 + temperature = 0.7 + presencePenalty = 0 + frequencyPenalty = 0 +) + +type aiReq struct { + Messages []aiMessage `json:"messages"` + MaxTokens float32 `json:"max_tokens"` + Temperature float32 `json:"temperature"` + FrequencyPenalty float32 `json:"frequency_penalty"` + PresencePenalty float32 `json:"presence_penalty"` + TopP float32 `json:"top_p"` +} + +func (ai *KubeAi) generateKubeRecipe(kubeName1, kubeName2 string) (string, error) { + url := fmt.Sprintf("%s/openai/deployments/%s/chat/completions?api-version=2024-02-15-preview", ai.Endpoint, ai.ModelId) + + postReq := aiReq{ + Messages: []aiMessage{ + { + Role: "system", + Content: baseRequest, + }, + { + Role: "user", + Content: ai.generateAiPrompt(kubeName1, kubeName2), + }, + }, + MaxTokens: maxTokens, + Temperature: temperature, + FrequencyPenalty: frequencyPenalty, + TopP: topP, + PresencePenalty: presencePenalty, + } + postBody, err := json.Marshal(postReq) + if err != nil { + log.Printf("THIS IS BAD %s", err) + return "", err + } + + req, err := http.NewRequest("POST", url, bytes.NewReader(postBody)) + if err != nil { + log.Printf("Cannot get a message or something like that mate: %s", err) + return "", err + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("api-key", ai.Apikey) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Printf("Cannot get a message or something like that mate: %s", err) + return "", err + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Printf("A nightmare has occurred, I cannot get any data :( %s", err) + return "", err + } + + var aiResponse openaiResp + err = json.Unmarshal([]byte(body), &aiResponse) + if err != nil { + log.Printf("The silly server sent %s, this is very bad %s", body, err) + return string(body), nil + } + + if len(aiResponse.Choices) == 0 { + log.Printf("The silly server sent %s, this is very bad", body) + return string(body), nil + } + + actualLegitMessage := aiResponse.Choices[0].Message.Content + + var aiResp2 aiResp + err = json.Unmarshal([]byte(actualLegitMessage), &aiResp2) + if err != nil { + log.Printf("The silly ai sent %s, this is very bad %s", actualLegitMessage, err) + return actualLegitMessage, nil + } + + if aiResp2.Name == "" { + log.Printf("The silly ai sent %s, this is very bad", actualLegitMessage) + return actualLegitMessage, nil + } + + ai.Metrics.IncrementGptRequests() + return aiResp2.Name, nil +} + +func (ai *KubeAi) GenerateKubeRecipe(kubeName1, kubeName2 string) (string, error) { + for { + ai.Lock.Lock() + if time.Since(ai.LastAccess) > apiRestTime { + ai.LastAccess = time.Now() + ai.Lock.Unlock() + break + } + ai.Lock.Unlock() + time.Sleep(apiRestTime - time.Since(ai.LastAccess)) + } + + res, err := ai.generateKubeRecipe(kubeName1, kubeName2) + if err != nil { + ai.Metrics.IncrementDalleErrors() + return "", err + } + return res, nil +} diff --git a/kube_cache/aiStuff/dalle.go b/kube_cache/aiStuff/dalle.go new file mode 100644 index 0000000..38264ea --- /dev/null +++ b/kube_cache/aiStuff/dalle.go @@ -0,0 +1,137 @@ +package aiStuff + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "net/http" + "os" + "time" + + imgresize "github.com/CosmicKube/kube_cache/imgResize" +) + +func (ai *KubeAi) generateDallePrompt(kubeName string) string { + return fmt.Sprintf("A flat tillable square texture that represents %s in pixel art for a video game, covers the full image", kubeName) +} + +const ( + dalleN = 1 + dalleSize = "1024x1024" +) + +type data struct { + Url string `json:"url"` +} + +type DalleResp struct { + Data []data `json:"data"` +} + +type dalleRequest struct { + Prompt string `json:"prompt"` + NumSamples int `json:"n"` + Size string `json:"size"` +} + +func (ai *KubeAi) generateDalleForKube(kubeName string) ([]byte, error) { + log.Printf("Generating Dalle for kube: %s", kubeName) + prompt := ai.generateDallePrompt(kubeName) + dalleReq := dalleRequest{ + Prompt: prompt, + NumSamples: dalleN, + Size: dalleSize, + } + + reqBytes, err := json.Marshal(dalleReq) + if err != nil { + log.Printf("Error marshalling dalle request: %s", err) + return nil, err + } + + url := fmt.Sprintf("%s/openai/deployments/Dalle3/images/generations?api-version=2024-02-15-preview", ai.Endpoint) + req, err := http.NewRequest("POST", url, bytes.NewReader(reqBytes)) + if err != nil { + log.Printf("Cannot get a message or something like that mate: %s", err) + return nil, err + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("api-key", ai.Apikey) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Printf("Cannot get a message or something like that mate: %s", err) + return nil, err + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Printf("A nightmare has occurred, I cannot get any data :( %s", err) + return nil, err + } + + var dalleResp DalleResp + err = json.Unmarshal(body, &dalleResp) + if err != nil { + log.Printf("Error unmarshalling dalle response: %s", err) + return nil, err + } + + if len(dalleResp.Data) == 0 { + log.Printf("The silly server sent %s, this is very bad", body) + return nil, errors.New("No data in response") + } + + log.Println("Downloading dalle response") + resp, err = http.Get(dalleResp.Data[0].Url) + if err != nil { + log.Printf("Error getting image from url: %s", err) + return nil, err + } + + body, err = io.ReadAll(resp.Body) + if err != nil { + log.Printf("Error reading image from response: %s", err) + return nil, err + } + return imgresize.ResizeImage(body) +} + +func (ai *KubeAi) GenerateDalleForKube(kubeName string) ([]byte, error) { + for { + ai.Lock.Lock() + if time.Since(ai.LastAccess) > apiRestTime { + ai.LastAccess = time.Now() + ai.Lock.Unlock() + break + } + ai.Lock.Unlock() + time.Sleep(apiRestTime - time.Since(ai.LastAccess)) + } + + ai.LastAccess = time.Now() + img, err := ai.generateDalleForKube(kubeName) + if err != nil { + ai.Metrics.IncrementDalleErrors() + log.Println("Cannot generate image falling back to default image") + defaultFile, err := os.Open("default.png") + if err != nil { + log.Printf("Error creating default file: %s", err) + return nil, err + } + + img, err := io.ReadAll(defaultFile) + if err != nil { + log.Printf("Error reading default image: %s", err) + return nil, err + } + + return img, nil + } + ai.Metrics.IncrementDalleRequests() + return img, nil +} diff --git a/kube_cache/cache.Dockerfile b/kube_cache/cache.Dockerfile new file mode 100644 index 0000000..a1f16c3 --- /dev/null +++ b/kube_cache/cache.Dockerfile @@ -0,0 +1,16 @@ +FROM debian:12.5-slim + +RUN apt-get update +RUN apt-get install ca-certificates -y +RUN apt-get upgrade -y + +RUN useradd -m app + +USER app +WORKDIR /home/app +COPY kube_cache/kube_cache . +COPY kube_cache/default.png . + +EXPOSE 8080 + +CMD ["./kube_cache"] diff --git a/kube_cache/default.png b/kube_cache/default.png new file mode 100644 index 0000000..747ecb6 Binary files /dev/null and b/kube_cache/default.png differ diff --git a/kube_cache/funny.js b/kube_cache/funny.js new file mode 100644 index 0000000..4af0cd8 --- /dev/null +++ b/kube_cache/funny.js @@ -0,0 +1,19 @@ +const kubes = "https://hack.djpiper28.co.uk/cache/kubes"; + +fetch(kubes) + .then((res) => res.json()) + .then(async (data) => { + for (let i = 0; i < data.length; i++) { + for (let j = 0; j < i; j++) { + const url = + "https://hack.djpiper28.co.uk/cache/kubeRecipeByIds/" + + data[i].id + + "/" + + data[j].id; + console.log(url); + await fetch(url) + .then((res) => console.log(res.json)) + .catch((err) => console.error(err)); + } + } + }); diff --git a/kube_cache/go.mod b/kube_cache/go.mod index e4754e8..53784a6 100644 --- a/kube_cache/go.mod +++ b/kube_cache/go.mod @@ -3,61 +3,53 @@ module github.com/CosmicKube/kube_cache go 1.22.0 require ( - github.com/KyleBanks/depth v1.2.1 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/gin-contrib/cors v1.5.0 + github.com/gin-gonic/gin v1.9.1 + github.com/google/uuid v1.6.0 + github.com/joho/godotenv v1.5.1 + github.com/zsais/go-gin-prometheus v0.1.0 + gorm.io/driver/postgres v1.5.6 + gorm.io/gorm v1.25.7 +) + +require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect - github.com/gin-contrib/cors v1.5.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.9.1 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.5 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.4.3 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/joho/godotenv v1.5.1 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.4 // indirect - github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/prometheus/client_golang v1.19.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/swaggo/gin-swagger v1.6.0 // indirect - github.com/swaggo/swag v1.8.12 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect - github.com/zsais/go-gin-prometheus v0.1.0 // indirect golang.org/x/arch v0.5.0 // indirect - golang.org/x/crypto v0.18.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.7.0 // indirect google.golang.org/protobuf v1.32.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gorm.io/driver/postgres v1.5.6 // indirect - gorm.io/gorm v1.25.7 // indirect ) diff --git a/kube_cache/go.sum b/kube_cache/go.sum index ca326e8..add72b7 100644 --- a/kube_cache/go.sum +++ b/kube_cache/go.sum @@ -1,21 +1,12 @@ -github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= -github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= @@ -23,6 +14,7 @@ github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= @@ -32,28 +24,18 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -69,26 +51,18 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -96,11 +70,11 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= @@ -110,24 +84,21 @@ github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSz github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= -github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= -github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w= -github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -135,57 +106,25 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ github.com/zsais/go-gin-prometheus v0.1.0 h1:bkLv1XCdzqVgQ36ScgRi09MA2UC1t3tAB6nsfErsGO4= github.com/zsais/go-gin-prometheus v0.1.0/go.mod h1:Slirjzuz8uM8Cw0jmPNqbneoqcUtY2GGjn2bEd4NRLY= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.5.6 h1:ydr9xEd5YAM0vxVDY0X139dyzNz10spDiDlC7+ibLeU= diff --git a/kube_cache/imgResize/imageResize.go b/kube_cache/imgResize/imageResize.go new file mode 100644 index 0000000..80eee34 --- /dev/null +++ b/kube_cache/imgResize/imageResize.go @@ -0,0 +1,57 @@ +package imgresize + +import ( + "bytes" + "image/jpeg" + "image/png" + "io" + "log" + "os" + + "github.com/nfnt/resize" +) + +const ( + dimX = 100 + dimY = 100 +) + +func ResizeImage(img []byte) ([]byte, error) { + log.Println("Creating temp file for image resize") + outputFile, err := os.CreateTemp(".", "resized-*.jpg") + if err != nil { + log.Printf("Error creating temp file: %s", err) + return nil, err + } + defer outputFile.Close() + + log.Printf("Resizing image, original size: %d", len(img)) + image, err := png.Decode(bytes.NewReader(img)) + if err != nil { + log.Printf("Error decoding image: %s", err) + return nil, err + } + + newImg := resize.Resize(dimX, dimY, image, resize.Lanczos3) + if err != nil { + log.Printf("Error encoding image: %s", err) + return nil, err + } + + log.Println("Encoding resized image") + err = jpeg.Encode(outputFile, newImg, nil) + if err != nil { + log.Printf("Error encoding image: %s", err) + return nil, err + } + + outputFile.Seek(0, 0) + newImageByes, err := io.ReadAll(outputFile) + if err != nil { + log.Printf("Error reading resized image: %s", err) + return nil, err + } + + log.Printf("Resized image size: %d", len(newImageByes)) + return newImageByes, nil +} diff --git a/kube_cache/main.go b/kube_cache/main.go index 3690063..0454c82 100644 --- a/kube_cache/main.go +++ b/kube_cache/main.go @@ -3,8 +3,12 @@ package main import ( "log" "os" + "strings" + "github.com/CosmicKube/kube_cache/aiStuff" + "github.com/CosmicKube/kube_cache/metrics" "github.com/CosmicKube/kube_cache/model" + "github.com/CosmicKube/kube_cache/server" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/joho/godotenv" @@ -12,28 +16,57 @@ import ( ) func main() { - log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime) + log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime) - log.Println("Reading config...") - err := godotenv.Load() - if err != nil { - log.Fatal(err) - } + log.Println("Reading config...") + err := godotenv.Load() + if err != nil { + log.Println(err) + } - log.Println("Using configuration for database...") - model.New(os.Getenv("DATABASE_URL")) + metrics := metrics.New() - log.Println("Starting server...") - router := gin.Default() + log.Println("Creating AI client...") + ai := aiStuff.New(metrics, os.Getenv("OPENAI_ENDPOINT"), + os.Getenv("OPENAI_API_KEY"), + os.Getenv("OPENAI_MODEL_ID")) + + log.Println("Using configuration for database...") + database := model.New(metrics, ai, os.Getenv("DATABASE_URL")) + + log.Println("Starting server...") + router := gin.Default() p := ginpromehteus.NewPrometheus("gin") + p.ReqCntURLLabelMappingFn = func(c *gin.Context) string { + url := c.Request.URL.Path + for _, p := range c.Params { + if p.Key == "id" { + url = strings.Replace(url, p.Value, ":id", 1) + break + } else if p.Key == "id1" { + url = strings.Replace(url, p.Value, ":id1", 1) + break + } else if p.Key == "id2" { + url = strings.Replace(url, p.Value, ":id2", 1) + break + } + } + return url + } - router.Use(cors.New(cors.Config{ - AllowOrigins: []string{"*"}, - AllowHeaders: []string{"*"}, - AllowOriginFunc: func(_ string) bool { - return true - }, - })) p.Use(router) + + router.Use(cors.New(cors.Config{ + AllowOrigins: []string{"*"}, + AllowHeaders: []string{"*"}, + AllowOriginFunc: func(_ string) bool { + return true + }, + })) + + log.Println("Start up the API") + server := server.New(metrics, database, ai) + server.Use(router) + log.Fatal(router.Run("0.0.0.0:8080")) } diff --git a/kube_cache/metrics/metrics.go b/kube_cache/metrics/metrics.go new file mode 100644 index 0000000..9f7c4a5 --- /dev/null +++ b/kube_cache/metrics/metrics.go @@ -0,0 +1,72 @@ +package metrics + +import ( + "fmt" + "sync" +) + +type Metrics struct { + Lock sync.Mutex + CacheHits, CacheMisses int + ImagesRetrieved int + DalleRequests int + GptRequests int + DalleErrors int + GptErrors int +} + +func New() *Metrics { + return &Metrics{} +} + +func (m *Metrics) IncrementCacheHits() { + m.Lock.Lock() + defer m.Lock.Unlock() + m.CacheHits++ +} + +func (m *Metrics) IncrementCacheMisses() { + m.Lock.Lock() + defer m.Lock.Unlock() + m.CacheMisses++ +} + +func (m *Metrics) IncrementImagesRetrieved() { + m.Lock.Lock() + defer m.Lock.Unlock() + m.ImagesRetrieved++ +} + +func (m *Metrics) IncrementDalleRequests() { + m.Lock.Lock() + defer m.Lock.Unlock() + m.DalleRequests++ +} + +func (m *Metrics) IncrementGptRequests() { + m.Lock.Lock() + defer m.Lock.Unlock() + m.GptRequests++ +} + +func (m *Metrics) IncrementDalleErrors() { + m.Lock.Lock() + defer m.Lock.Unlock() + m.DalleErrors++ +} + +func (m *Metrics) IncrementGptErrors() { + m.Lock.Lock() + defer m.Lock.Unlock() + m.GptErrors++ +} + +func (m *Metrics) String() string { + return fmt.Sprintf(`kube_cache_hits %d +kube_cache_misses %d +kube_images_retrieved %d +kube_dalle_requests %d +kube_gpt_requests %d +kube_dalle_errors %d +kube_gpt_errors %d`, m.CacheHits, m.CacheMisses, m.ImagesRetrieved, m.DalleRequests, m.GptRequests, m.DalleErrors, m.GptErrors) +} diff --git a/kube_cache/model/model.go b/kube_cache/model/model.go index e53c5e1..f53da92 100644 --- a/kube_cache/model/model.go +++ b/kube_cache/model/model.go @@ -3,35 +3,49 @@ package model import ( "log" + "github.com/CosmicKube/kube_cache/aiStuff" + "github.com/CosmicKube/kube_cache/metrics" "github.com/google/uuid" "gorm.io/driver/postgres" "gorm.io/gorm" ) +func SortKubesStr(kube1, kube2 *string) { + if *kube1 > *kube2 { + *kube1, *kube2 = *kube2, *kube1 + } +} + +func SortKubesUuid(kube1, kube2 *uuid.UUID) { + if kube1.String() > kube2.String() { + *kube1, *kube2 = *kube2, *kube1 + } +} + type Kube struct { - Name string `json:"name"` - Id uuid.UUID `gorm:"primaryKey" json:"id"` - // Image []byte `json:"image"` + Name string `gorm:"unique;not null" json:"name"` + Id uuid.UUID `gorm:"primaryKey" json:"id"` + Image []byte `gorm:"not null" json:"-"` } type KubeRecipe struct { - Id uuid.UUID `gorm:"primaryKey" json:"id"` - Output uuid.UUID `json:"outputId" gorm:"index:idx_output_id"` - OutputKube *Kube `json:"outputKube" gorm:"foreignKey:Output;references:Id"` - Ingredients []KubeRecipeLines `json:"ingredients"` -} + Id uuid.UUID `gorm:"primaryKey" json:"id"` + Output uuid.UUID `json:"outputId" gorm:"index:idx_output_id;not null"` + OutputKube *Kube `json:"outputKube" gorm:"foreignKey:Output;references:Id"` + + Kube1Id uuid.UUID `json:"kube1_id" gorm:"index:idx_kube1_id;not null"` + Kube1 *Kube `json:"-" gorm:"foreignKey:Kube1Id;references:Id"` -type KubeRecipeLines struct { - KubeRecipeId uuid.UUID `json:"kube_recipe_id" gorm:"index:idx_kube_recipe_id"` - KubeId string `json:"kube_id" gorm:"index:idx_kube_id"` - Kube *Kube `json:"kube" gorm:"foreignKey:KubeId;references:Id"` + Kube2Id uuid.UUID `json:"kube2_id" gorm:"index:idx_kube2_id;not null"` + Kube2 *Kube `json:"-" gorm:"foreignKey:Kube2Id;references:Id"` } type Database struct { - Db *gorm.DB + Db *gorm.DB + Metrics *metrics.Metrics } -func New(url string) *Database { +func New(metrics *metrics.Metrics, ai *aiStuff.KubeAi, url string) *Database { log.Println("Connecting to database...") db, err := gorm.Open(postgres.Open(url), &gorm.Config{ PrepareStmt: true, @@ -47,8 +61,23 @@ func New(url string) *Database { log.Fatal(err) } + database := &Database{Db: db, Metrics: metrics} + log.Println("Seeding database...") + database.seed(ai) + log.Println("Migrated database successfully") - return &Database{Db: db} + return database +} + +func (db *Database) SetKubeImage(kube Kube, image []byte) error { + result := db.Db.Model(&kube).Update("image", image) + return result.Error +} + +func (db *Database) GetKubeImage(id string) ([]byte, error) { + var kube Kube + result := db.Db.First(&kube, "id = ?", id) + return kube.Image, result.Error } func (db *Database) GetKube(id string) (Kube, error) { @@ -56,3 +85,60 @@ func (db *Database) GetKube(id string) (Kube, error) { result := db.Db.First(&kube, "id = ?", id) return kube, result.Error } + +func (db *Database) GetAllKubeRecipes() ([]KubeRecipe, error) { + var recipes []KubeRecipe + result := db.Db.Find(&recipes) + return recipes, result.Error +} + +func (db *Database) GetAllKubes() ([]Kube, error) { + var kubes []Kube + result := db.Db.Find(&kubes) + return kubes, result.Error +} + +func (db *Database) GetKubeRecipe(kube1, kube2 string) (KubeRecipe, error) { + SortKubesStr(&kube1, &kube2) + + var recipe KubeRecipe + result := db.Db.First(&recipe, "kube1_id = ? AND kube2_id = ?", kube1, kube2) + return recipe, result.Error +} + +func (db *Database) SetKubeRecipe(kube1, kube2 Kube, newKube string, image []byte) error { + kube1Id := kube1.Id + kube2Id := kube2.Id + SortKubesUuid(&kube1Id, &kube2Id) + + log.Printf("Setting kube recipe: %s + %s = %s", kube1.Name, kube2.Name, newKube) + err := db.Db.Transaction(func(tx *gorm.DB) error { + newKubeObject := Kube{Name: newKube, + Id: uuid.New(), + Image: image} + err := tx.Create(&newKubeObject).Error + if err != nil { + log.Printf("Cannot create new kube: %s", err) + return err + } + + recipe := KubeRecipe{ + Id: uuid.New(), + Output: newKubeObject.Id, + Kube1Id: kube1Id, + Kube2Id: kube2Id, + } + err = tx.Create(&recipe).Error + if err != nil { + log.Printf("Cannot create new kube recipe: %s", err) + return err + } + return nil + }) + + if err != nil { + log.Print("Saving the kube recipe failed") + return err + } + return nil +} diff --git a/kube_cache/model/seed.go b/kube_cache/model/seed.go new file mode 100644 index 0000000..78bd60d --- /dev/null +++ b/kube_cache/model/seed.go @@ -0,0 +1,128 @@ +package model + +import ( + "errors" + "log" + + "github.com/CosmicKube/kube_cache/aiStuff" + "github.com/google/uuid" + "gorm.io/gorm" +) + +func insertKube(ai *aiStuff.KubeAi, name string, tx *gorm.DB) error { + log.Printf("Checking if kube exists: %s", name) + if (tx.Where("name = ?", name).First(&Kube{}).RowsAffected > 0) { + return errors.New("Kube already exists") + } + + log.Printf("Inserting kube: %s", name) + image, err := ai.GenerateDalleForKube(name) + if err != nil { + log.Printf("Error generating Dalle for kube: %s", err) + return err + } + kube := Kube{Name: name, Id: uuid.New(), Image: image} + return tx.Create(&kube).Error +} + +func insertKubeRecipe(ai *aiStuff.KubeAi, kube1, kube2, output string, tx *gorm.DB) error { + log.Printf("Inserting kube recipe: %s + %s = %s", kube1, kube2, output) + kube1Row := Kube{} + kube2Row := Kube{} + outputRow := Kube{} + + tx.First(&kube1Row, "name = ?", kube1) + tx.First(&kube2Row, "name = ?", kube2) + + kube1Id := kube1Row.Id + kube2Id := kube2Row.Id + SortKubesUuid(&kube1Id, &kube2Id) + + err := insertKube(ai, output, tx) + if err != nil { + return err + } + tx.First(&outputRow, "name = ?", output) + + kubeRecipe := KubeRecipe{ + Id: uuid.New(), + Output: outputRow.Id, + Kube1Id: kube1Id, + Kube2Id: kube2Id, + } + + return tx.Create(&kubeRecipe).Error +} + +type recipe struct { + kube1 string + kube2 string + output string +} + +func (d *Database) seed(ai *aiStuff.KubeAi) { + err := d.Db.Transaction(func(tx *gorm.DB) error { + kubes := []string{ + "hydrogen", + "oxygen", + "nitrogen", + "calcium", + "iron", + "aluminium", + "uranium", + "sodium", + "chlorine", + "light", + "time", + "silicon", + } + + for _, kube := range kubes { + if err := insertKube(ai, kube, tx); err != nil { + return err + } + } + + recipes := []recipe{ + {"hydrogen", "oxygen", "water"}, + {"water", "chlorine", "tap water"}, + {"sodium", "chlorine", "salt"}, + {"water", "salt", "sea water"}, + {"nitrogen", "oxygen", "air"}, + {"iron", "water", "rust"}, + {"silicon", "aluminium", "feldspar"}, + {"feldspar", "silicon", "sand"}, + {"sand", "water", "dirt"}, + {"sand", "sea water", "beach"}, + {"dirt", "water", "earth"}, + {"earth", "air", "life"}, + {"life", "time", "age"}, + {"uranium", "water", "energy"}, + {"sand", "time", "rock"}, + {"rock", "energy", "fire"}, + {"fire", "sand", "glass"}, + } + + for _, recipe := range recipes { + if err := insertKubeRecipe(ai, recipe.kube1, recipe.kube2, recipe.output, tx); err != nil { + return err + } + } + return nil + }) + + if err != nil { + log.Printf("Seeding database failed: %s", err) + } + + var kubes, recipes int64 + d.Db.Model(&Kube{}).Count(&kubes) + d.Db.Model(&KubeRecipe{}).Count(&recipes) + + log.Printf("There are %d kubes and %d recipes in the database", kubes, recipes) + if kubes == 0 || recipes == 0 { + log.Fatal("Seeding database failed - no kubes or recipes") + } + + log.Println("Database successfully seeded") +} diff --git a/kube_cache/server/cache.go b/kube_cache/server/cache.go index 18090da..ed67cb4 100644 --- a/kube_cache/server/cache.go +++ b/kube_cache/server/cache.go @@ -1,9 +1,161 @@ package server import ( + "log" + + "github.com/CosmicKube/kube_cache/aiStuff" + "github.com/CosmicKube/kube_cache/metrics" "github.com/CosmicKube/kube_cache/model" + "github.com/gin-gonic/gin" ) type Server struct { Database *model.Database + Ai *aiStuff.KubeAi + Metrics *metrics.Metrics +} + +func New(metrics *metrics.Metrics, database *model.Database, ai *aiStuff.KubeAi) *Server { + return &Server{Database: database, Ai: ai, Metrics: metrics} +} + +func (s *Server) Use(engine *gin.Engine) { + engine.GET("/", s.Index) + engine.GET("/cache_metrics", s.CacheMetrics) + engine.GET("/kubes", s.GetAllKubes) + engine.GET("/kubeRecipes", s.GetAllKubeRecipes) + engine.GET("/kubeById/:id", s.GetKube) + engine.GET("/kubeImageById/:id", s.GetKubeImage) + engine.GET("/kubeImageByIdNew/:id", s.RegenerateImage) + engine.GET("/kubeRecipeByIds/:id1/:id2", s.GetKubeRecipe) +} + +func (s *Server) CacheMetrics(c *gin.Context) { + c.Data(200, "text/plain", []byte(s.Metrics.String())) +} + +func (s *Server) RegenerateImage(c *gin.Context) { + id := c.Param("id") + kube, err := s.Database.GetKube(id) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + image, err := s.Ai.GenerateDalleForKube(kube.Name) + if err != nil { + log.Printf("Error generating Dalle for kube: %s", err) + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + err = s.Database.SetKubeImage(kube, image) + if err != nil { + log.Printf("Cannot save kube image: %s", err) + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(200, gin.H{"message": "Image regenerated"}) +} + +func (s *Server) GetAllKubeRecipes(c *gin.Context) { + recipes, err := s.Database.GetAllKubeRecipes() + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(200, recipes) +} + +func (s *Server) GetKubeImage(c *gin.Context) { + id := c.Param("id") + image, err := s.Database.GetKubeImage(id) + if err != nil { + log.Printf("Cannot get kube image: %s", err) + c.JSON(500, gin.H{"error": err.Error()}) + s.Metrics.IncrementCacheMisses() + return + } + c.Data(200, "image/png", image) + c.Header("Cache-Control", cacheControlHeader) + s.Metrics.IncrementCacheHits() +} + +func (s *Server) GetAllKubes(c *gin.Context) { + kubes, err := s.Database.GetAllKubes() + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(200, kubes) +} + +func (s *Server) GetKube(c *gin.Context) { + id := c.Param("id") + kube, err := s.Database.GetKube(id) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + s.Metrics.IncrementCacheMisses() + return + } + c.JSON(200, kube) + c.Header("Cache-Control", cacheControlHeader) + s.Metrics.IncrementCacheHits() +} + +// 24 hours +const cacheControlHeader = "max-age=86400" + +func (s *Server) GetKubeRecipe(c *gin.Context) { + id1 := c.Param("id1") + id2 := c.Param("id2") + recipe, err := s.Database.GetKubeRecipe(id1, id2) + if err != nil { + s.Metrics.IncrementCacheMisses() + kube1, err := s.Database.GetKube(id1) + if err != nil { + log.Printf("Cannot get kube: %s", err) + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + kube2, err := s.Database.GetKube(id2) + if err != nil { + log.Printf("Cannot get kube: %s", err) + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + newKube, err := s.Ai.GenerateKubeRecipe(kube1.Name, kube2.Name) + if err != nil { + log.Printf("Cannot generate kube recipe: %s", err) + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + image, err := s.Ai.GenerateDalleForKube(newKube) + if err != nil { + log.Printf("Error generating Dalle for kube: %s", err) + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + err = s.Database.SetKubeRecipe(kube1, kube2, newKube, image) + if err != nil { + log.Printf("Cannot save kube recipe: %s", err) + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + recipe, err = s.Database.GetKubeRecipe(id1, id2) + if err != nil { + log.Printf("Cannot get kube recipe: %s", err) + c.JSON(500, gin.H{"error": err.Error()}) + return + } + } else { + s.Metrics.IncrementCacheHits() + } + c.Header("Cache-Control", cacheControlHeader) + c.JSON(200, recipe) } diff --git a/kube_cache/server/index.go b/kube_cache/server/index.go new file mode 100644 index 0000000..a0fa114 --- /dev/null +++ b/kube_cache/server/index.go @@ -0,0 +1,45 @@ +package server + +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +func (s *Server) Index(c *gin.Context) { + kubes, err := s.Database.GetAllKubes() + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + kubesHtml := "" + for _, kube := range kubes { + kubesHtml += fmt.Sprintf(`
+

%s

+ +
`, kube.Name, kube.Id, kube.Id) + } + + body := fmt.Sprintf(` + + Kube Cache + + +

Kube Cache

+

Endpoints

+ +

Kubes

+
+ %s +
+`, kubesHtml) + + c.Data(200, "text/html", []byte(body)) +} diff --git a/plugins.txt b/plugins.txt deleted file mode 100644 index e69de29..0000000