From 2f366348952b50c01234902602d5e64eb072934f Mon Sep 17 00:00:00 2001 From: Jordan Swan Date: Wed, 26 Feb 2025 09:34:22 -0700 Subject: [PATCH 1/4] chore(api): update cors configuration to allow vite-client to connect to socket --- api/index.js | 5 ++--- vite-client/index.html | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/api/index.js b/api/index.js index 67a62b7..1f88c46 100644 --- a/api/index.js +++ b/api/index.js @@ -9,8 +9,7 @@ const app = express(); const PORT = 5000; // Middleware -app.use(cors({ origin: 'http://localhost:5173', credentials: true })); -app.use(cors({ origin: 'http://localhost:5174', credentials: true })); +app.use(cors({ origin: ['http://localhost:5173', 'http://localhost:5174'], credentials: true })); app.use(express.json()); // http server @@ -19,7 +18,7 @@ const server = http.createServer(app); // configure socket.io const io = new Server(server, { cors: { - origin: 'http://localhost:5174', + origin: ['http://localhost:5173', 'http://localhost:5174'], methods: ['GET', 'POST'], credentials: true, }, diff --git a/vite-client/index.html b/vite-client/index.html index 0c589ec..42296be 100644 --- a/vite-client/index.html +++ b/vite-client/index.html @@ -4,7 +4,7 @@ - Vite + React + D&D AI Assistant
From 62252e09a158f2137c22b52962a52e80f320a6e5 Mon Sep 17 00:00:00 2001 From: Jordan Swan Date: Wed, 26 Feb 2025 11:22:48 -0700 Subject: [PATCH 2/4] feat: added typescript support for the api --- api/{index.js => index.ts} | 26 +++++++++++++++++--------- api/package.json | 14 ++++++++++---- api/tsconfig.json | 18 ++++++++++++++++++ api/types/apiTypes.d.ts | 30 ++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 13 deletions(-) rename api/{index.js => index.ts} (77%) create mode 100644 api/tsconfig.json create mode 100644 api/types/apiTypes.d.ts diff --git a/api/index.js b/api/index.ts similarity index 77% rename from api/index.js rename to api/index.ts index 1f88c46..744d5b1 100644 --- a/api/index.js +++ b/api/index.ts @@ -1,10 +1,13 @@ -require('dotenv').config(); -const express = require('express'); -const cors = require('cors'); -const { OpenAI } = require('openai'); -const http = require('http'); -const { Server } = require('socket.io'); +import {ClientToServerEvents, InterServerEvents, ServerToClientEvents, SocketData} from "apiTypes"; +import { config as dotenvConfig } from "dotenv"; +import { Server } from "socket.io"; +import express from "express"; +import cors from "cors"; +import { OpenAI } from "openai"; +import http from "http"; + +dotenvConfig() const app = express(); const PORT = 5000; @@ -16,7 +19,12 @@ app.use(express.json()); const server = http.createServer(app); // configure socket.io -const io = new Server(server, { +const io = new Server< + ClientToServerEvents, + ServerToClientEvents, + InterServerEvents, + SocketData +>(server, { cors: { origin: ['http://localhost:5173', 'http://localhost:5174'], methods: ['GET', 'POST'], @@ -33,7 +41,7 @@ io.on('connection', (socket) => { console.log('New client connected:', socket.id); // Receive prompt along with its unique ID from the client - socket.on('send_prompt', async ({ id, prompt }) => { + socket.on("send_prompt", async ({ id, prompt }) => { try { const response = await openai.chat.completions.create({ model: 'gpt-4', @@ -65,4 +73,4 @@ io.on('connection', (socket) => { // Start Server server.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); -}); +}); diff --git a/api/package.json b/api/package.json index 6700f9f..fd98290 100644 --- a/api/package.json +++ b/api/package.json @@ -2,10 +2,11 @@ "name": "server", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "dist/index.js", "scripts": { - "start": "node index.js", - "dev": "nodemon index.js" + "build": "npx tsc", + "start": "node dist/index.js", + "dev": "nodemon index.ts" }, "keywords": [], "author": "", @@ -19,6 +20,11 @@ "socket.io": "^4.8.1" }, "devDependencies": { - "nodemon": "^3.1.9" + "@types/express": "^5.0.0", + "@types/node": "^22.13.5", + "concurrently": "^9.1.2", + "nodemon": "^3.1.9", + "ts-node": "^10.9.2", + "typescript": "^5.7.3" } } diff --git a/api/tsconfig.json b/api/tsconfig.json new file mode 100644 index 0000000..f3d5e7b --- /dev/null +++ b/api/tsconfig.json @@ -0,0 +1,18 @@ +/* Visit https://aka.ms/tsconfig to read more about this file */ +{ + "compilerOptions": { + "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "module": "NodeNext", /* Specify what module code is generated. */ + "moduleResolution": "nodenext", + "baseUrl": "./", + "paths": { /* Specify multiple folders that act like './node_modules/@types'. */ + "*": [ + "./types/*.d.ts" + ] + }, + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, +} diff --git a/api/types/apiTypes.d.ts b/api/types/apiTypes.d.ts new file mode 100644 index 0000000..1df5084 --- /dev/null +++ b/api/types/apiTypes.d.ts @@ -0,0 +1,30 @@ +export interface ServerToClientEvents { + noArg: () => void; + basicEmit: (a: number, b: string, c: Buffer) => void; + withAck: (d: string, callback: (e: number) => void) => void; + ai_response: (data: AiResponseData) => void; +} + +export interface ClientToServerEvents { + hello: () => void; + send_prompt: (args: SendPromptData) => void; +} + +export interface InterServerEvents { + ping: () => void; +} + +export interface SocketData { + name: string; + age: number; +} + +export interface SendPromptData { + id: string; + prompt: string; +} + +export interface AiResponseData { + id: string; + response: string | null; +} From 4068f51f6d3560c5cab58e789bb6be558f43b96e Mon Sep 17 00:00:00 2001 From: Jordan Swan Date: Wed, 26 Feb 2025 21:35:52 -0700 Subject: [PATCH 3/4] feat: removed CORS allowances for localhost:5174, updated html title page to be more appropriate, added prettier for code formatting --- api/index.ts | 56 ++++++++++++++++++++++++++--------------- api/package.json | 1 + api/types/apiTypes.d.ts | 26 +++++++++---------- vite-client/index.html | 2 +- 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/api/index.ts b/api/index.ts index 744d5b1..0c16808 100644 --- a/api/index.ts +++ b/api/index.ts @@ -1,4 +1,9 @@ -import {ClientToServerEvents, InterServerEvents, ServerToClientEvents, SocketData} from "apiTypes"; +import { + ClientToServerEvents, + InterServerEvents, + ServerToClientEvents, + SocketData, +} from "apiTypes"; import { config as dotenvConfig } from "dotenv"; import { Server } from "socket.io"; @@ -7,12 +12,17 @@ import cors from "cors"; import { OpenAI } from "openai"; import http from "http"; -dotenvConfig() +dotenvConfig(); const app = express(); const PORT = 5000; // Middleware -app.use(cors({ origin: ['http://localhost:5173', 'http://localhost:5174'], credentials: true })); +app.use( + cors({ + origin: ["http://localhost:5173"], + credentials: true, + }), +); app.use(express.json()); // http server @@ -20,14 +30,14 @@ const server = http.createServer(app); // configure socket.io const io = new Server< - ClientToServerEvents, - ServerToClientEvents, - InterServerEvents, - SocketData + ClientToServerEvents, + ServerToClientEvents, + InterServerEvents, + SocketData >(server, { cors: { - origin: ['http://localhost:5173', 'http://localhost:5174'], - methods: ['GET', 'POST'], + origin: ["http://localhost:5173"], + methods: ["GET", "POST"], credentials: true, }, }); @@ -37,20 +47,20 @@ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); -io.on('connection', (socket) => { - console.log('New client connected:', socket.id); +io.on("connection", (socket) => { + console.log("New client connected:", socket.id); // Receive prompt along with its unique ID from the client socket.on("send_prompt", async ({ id, prompt }) => { try { const response = await openai.chat.completions.create({ - model: 'gpt-4', + model: "gpt-4", messages: [ - { role: 'user', content: prompt }, + { role: "user", content: prompt }, { - role: 'system', + role: "system", content: - 'You are overhearing a game master running a Tabletop RPG game, briefly predict and provide creative ideas for what the game master might say next. You are limited to 20 tokens', + "You are overhearing a game master running a Tabletop RPG game, briefly predict and provide creative ideas for what the game master might say next. You are limited to 20 tokens", }, ], max_tokens: 20, @@ -58,15 +68,21 @@ io.on('connection', (socket) => { }); // Emit response along with the original ID for client-side matching - socket.emit('ai_response', { id, response: response.choices[0].message.content }); + socket.emit("ai_response", { + id, + response: response.choices[0].message.content, + }); } catch (error) { - console.error('error from OpenAI:', error); - socket.emit('ai_response', { id, response: 'Error generating suggestion.' }); + console.error("error from OpenAI:", error); + socket.emit("ai_response", { + id, + response: "Error generating suggestion.", + }); } }); - socket.on('disconnect', () => { - console.log('Client disconnected:', socket.id); + socket.on("disconnect", () => { + console.log("Client disconnected:", socket.id); }); }); diff --git a/api/package.json b/api/package.json index fd98290..2d6e0ea 100644 --- a/api/package.json +++ b/api/package.json @@ -24,6 +24,7 @@ "@types/node": "^22.13.5", "concurrently": "^9.1.2", "nodemon": "^3.1.9", + "prettier": "^3.5.2", "ts-node": "^10.9.2", "typescript": "^5.7.3" } diff --git a/api/types/apiTypes.d.ts b/api/types/apiTypes.d.ts index 1df5084..961819b 100644 --- a/api/types/apiTypes.d.ts +++ b/api/types/apiTypes.d.ts @@ -1,30 +1,30 @@ export interface ServerToClientEvents { - noArg: () => void; - basicEmit: (a: number, b: string, c: Buffer) => void; - withAck: (d: string, callback: (e: number) => void) => void; - ai_response: (data: AiResponseData) => void; + noArg: () => void; + basicEmit: (a: number, b: string, c: Buffer) => void; + withAck: (d: string, callback: (e: number) => void) => void; + ai_response: (data: AiResponseData) => void; } export interface ClientToServerEvents { - hello: () => void; - send_prompt: (args: SendPromptData) => void; + hello: () => void; + send_prompt: (args: SendPromptData) => void; } export interface InterServerEvents { - ping: () => void; + ping: () => void; } export interface SocketData { - name: string; - age: number; + name: string; + age: number; } export interface SendPromptData { - id: string; - prompt: string; + id: string; + prompt: string; } export interface AiResponseData { - id: string; - response: string | null; + id: string; + response: string | null; } diff --git a/vite-client/index.html b/vite-client/index.html index 42296be..72e104a 100644 --- a/vite-client/index.html +++ b/vite-client/index.html @@ -4,7 +4,7 @@ - D&D AI Assistant + Scrying AI - DEV
From eff8f5aebb497c644d60c4fe59130106a449348b Mon Sep 17 00:00:00 2001 From: Jesse Bergerstock Date: Mon, 17 Mar 2025 14:45:00 -0400 Subject: [PATCH 4/4] feat: init backend shared tfstate --- .gitignore | 8 ++++- .../dev/backend.tf | 0 .../{dns => deployments/dev}/main.tf | 0 .../dev/outputs.tf | 0 .../dev/terraform.tfvars | 0 .../dev/variables.tf | 0 iac/terraform/deployments/shared/backend.tf | 8 +++++ iac/terraform/deployments/shared/providers.tf | 15 ++++++++++ iac/terraform/deployments/shared/tfstate.tf | 30 +++++++++++++++++++ iac/terraform/environments/dev/main.tf | 0 10 files changed, 60 insertions(+), 1 deletion(-) rename iac/terraform/{environments => deployments}/dev/backend.tf (100%) rename iac/terraform/{dns => deployments/dev}/main.tf (100%) rename iac/terraform/{environments => deployments}/dev/outputs.tf (100%) rename iac/terraform/{environments => deployments}/dev/terraform.tfvars (100%) rename iac/terraform/{environments => deployments}/dev/variables.tf (100%) create mode 100644 iac/terraform/deployments/shared/backend.tf create mode 100644 iac/terraform/deployments/shared/providers.tf create mode 100644 iac/terraform/deployments/shared/tfstate.tf delete mode 100644 iac/terraform/environments/dev/main.tf diff --git a/.gitignore b/.gitignore index f92e45a..0b19c9c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,10 @@ yarn-error.log* # Dependency directories package-lock.json pnpm-lock.yaml -yarn.lock \ No newline at end of file +yarn.lock + +# Terraform +.terraform/ +*.tfstate +*.tfstate.backup +.terraform.lock.hcl \ No newline at end of file diff --git a/iac/terraform/environments/dev/backend.tf b/iac/terraform/deployments/dev/backend.tf similarity index 100% rename from iac/terraform/environments/dev/backend.tf rename to iac/terraform/deployments/dev/backend.tf diff --git a/iac/terraform/dns/main.tf b/iac/terraform/deployments/dev/main.tf similarity index 100% rename from iac/terraform/dns/main.tf rename to iac/terraform/deployments/dev/main.tf diff --git a/iac/terraform/environments/dev/outputs.tf b/iac/terraform/deployments/dev/outputs.tf similarity index 100% rename from iac/terraform/environments/dev/outputs.tf rename to iac/terraform/deployments/dev/outputs.tf diff --git a/iac/terraform/environments/dev/terraform.tfvars b/iac/terraform/deployments/dev/terraform.tfvars similarity index 100% rename from iac/terraform/environments/dev/terraform.tfvars rename to iac/terraform/deployments/dev/terraform.tfvars diff --git a/iac/terraform/environments/dev/variables.tf b/iac/terraform/deployments/dev/variables.tf similarity index 100% rename from iac/terraform/environments/dev/variables.tf rename to iac/terraform/deployments/dev/variables.tf diff --git a/iac/terraform/deployments/shared/backend.tf b/iac/terraform/deployments/shared/backend.tf new file mode 100644 index 0000000..021f471 --- /dev/null +++ b/iac/terraform/deployments/shared/backend.tf @@ -0,0 +1,8 @@ +terraform { + backend "azurerm" { + resource_group_name = "rg-tfstate" + storage_account_name = "stscryingtfstate" + container_name = "shared" + key = "terraform.tfstate" + } +} diff --git a/iac/terraform/deployments/shared/providers.tf b/iac/terraform/deployments/shared/providers.tf new file mode 100644 index 0000000..b1b64bb --- /dev/null +++ b/iac/terraform/deployments/shared/providers.tf @@ -0,0 +1,15 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>3.0.0" + } + } +} + +provider "azurerm" { + features {} + subscription_id = "6f889e85-5bc7-4b1e-a897-17aa4d0794a1" +} diff --git a/iac/terraform/deployments/shared/tfstate.tf b/iac/terraform/deployments/shared/tfstate.tf new file mode 100644 index 0000000..3d26135 --- /dev/null +++ b/iac/terraform/deployments/shared/tfstate.tf @@ -0,0 +1,30 @@ +resource "azurerm_resource_group" "tfstate" { + name = "rg-tfstate" + location = "East US" +} + +resource "azurerm_storage_account" "tfstate" { + name = "stscryingtfstate" + resource_group_name = azurerm_resource_group.tfstate.name + location = azurerm_resource_group.tfstate.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_container" "tfstate" { + name = "shared" + storage_account_name = azurerm_storage_account.tfstate.name +} + +# Admin-only access to shared state +# resource "azurerm_role_assignment" "tfstate_admins" { +# scope = azurerm_storage_account.tfstate.id +# role_definition_name = "Storage Blob Data Owner" +# principal_id = var.admin_group_id # Only Admins can modify +# } + +# resource "azurerm_role_assignment" "tfstate_read_only" { +# scope = azurerm_storage_account.tfstate.id +# role_definition_name = "Storage Blob Data Reader" +# principal_id = var.developer_group_id # Devs can only read +# } diff --git a/iac/terraform/environments/dev/main.tf b/iac/terraform/environments/dev/main.tf deleted file mode 100644 index e69de29..0000000