updating readme
This commit is contained in:
parent
f6b4b5a075
commit
e85e210ad6
8 changed files with 54 additions and 41 deletions
28
README.md
28
README.md
|
|
@ -6,7 +6,7 @@ Realtime AI Speech powered by OpenAI Realtime API, ESP32, Secure WebSockets, and
|
|||
<div align="center">
|
||||
|
||||
[](https://discord.gg/KJWxDPBRUj)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)   
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)   
|
||||

|
||||

|
||||

|
||||
|
|
@ -25,8 +25,9 @@ https://github.com/user-attachments/assets/aa60e54c-5847-4a68-80b5-5d6b1a5b9328
|
|||
|
||||
## Getting Started
|
||||
|
||||
1. Set up your Local Supabase Backend. From the root directory, run:
|
||||
1. Install [Supabase CLI](https://supabase.com/docs/guides/local-development/cli/getting-started) and set up your Local Supabase Backend. From the root directory, run:
|
||||
```bash
|
||||
brew install supabase/tap/supabase
|
||||
supabase start # Starts your local Supabase server with the default migrations and seed data.
|
||||
```
|
||||
|
||||
|
|
@ -34,10 +35,18 @@ supabase start # Starts your local Supabase server with the default migrations a
|
|||
```bash
|
||||
cd frontend-nextjs
|
||||
npm install
|
||||
|
||||
# Set your environment variables
|
||||
cp .env.example .env.local
|
||||
# NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
|
||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=<your_supabase_anon_key>
|
||||
# OPENAI_API_KEY=<your_openai_api_key>
|
||||
|
||||
# Run the development server
|
||||
npm run dev
|
||||
```
|
||||
|
||||
3. Add your ESP32-S3 Device MAC Address to the Settings page in the NextJS Frontend. This links your device to your account.
|
||||
3. Add your ESP32-S3 Device MAC Address to the [Settings page](http://localhost:3000/home/settings) in the NextJS Frontend. This links your device to your account.
|
||||
To find your ESP32-S3 Device's MAC Address, build and upload `test/print_mac_address_test.cpp` using PlatformIO.
|
||||
|
||||
4. Add your OpenAI API Key in the `server-deno/.env` and `frontend-nextjs/.env.local` file.
|
||||
|
|
@ -47,16 +56,27 @@ OPENAI_API_KEY=<your_openai_api_key>
|
|||
|
||||
5. Start the Deno server. ([See the Deno server README](server-deno/README.md))
|
||||
```bash
|
||||
# Navigate to the server directory
|
||||
cd server-deno
|
||||
|
||||
# Set your environment variables
|
||||
cp .env.example .env
|
||||
# NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
|
||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=<your_supabase_anon_key>
|
||||
# OPENAI_API_KEY=<your_openai_api_key>
|
||||
|
||||
# Run the server at port 8000
|
||||
deno run -A --env-file=.env main.ts
|
||||
```
|
||||
|
||||
5. Set up your ESP32 Arduino Client. ([See the ESP32 README](firmware-arduino/README.md)) On PlatformIO, first `Build` the project, then `Upload` the project to your ESP32.
|
||||
|
||||
6. The ESP32 should open an AP `ELATO-DEVICE` to connect to Wifi. Connect to it and go to `http://192.168.4.1` to configure the device.
|
||||
6. The ESP32 should open an AP `ELATO-DEVICE` to connect to Wifi. Connect to it and go to `http://192.168.4.1` to configure the device wifi.
|
||||
|
||||
7. Once your Wifi is configured, turn the device off and on again and it should connect to your Wifi and the Deno edge server.
|
||||
|
||||
8. Now you can talk to your AI Character!
|
||||
|
||||
## 📌 Project Architecture
|
||||
|
||||
ElatoAI consists of three main components:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ This firmware turns your ESP32 device into a WebSocket audio client for Elato, e
|
|||
|
||||
1. Install Visual Studio Code and the PlatformIO extension
|
||||
2. Clone this repository
|
||||
3. Open the project folder in PlatformIO
|
||||
3. Open the project folder in PlatformIO. `Open > Open Project > firmware-arduino`
|
||||
4. Edit `src/Config.cpp` with your server details:
|
||||
- If using locally: Set your computer's IP address in `ws_server` and `backend_server`
|
||||
- If using production: Ensure proper certificates are set
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ bool factory_reset_status = false;
|
|||
*/
|
||||
|
||||
#ifdef DEV_MODE
|
||||
const char *ws_server = "10.2.1.187";
|
||||
const char *ws_server = "10.2.1.136";
|
||||
const uint16_t ws_port = 8000;
|
||||
const char *ws_path = "/";
|
||||
// Backend server details
|
||||
const char *backend_server = "10.2.1.187";
|
||||
const char *backend_server = "10.2.1.136";
|
||||
const uint16_t backend_port = 3000;
|
||||
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ extern const uint16_t backend_port;
|
|||
extern const uint32_t SAMPLE_RATE;
|
||||
|
||||
// ---------- Development ------------
|
||||
// #define DEV_MODE
|
||||
#define DEV_MODE
|
||||
#define TOUCH_MODE
|
||||
|
||||
// ----------------- Pin Definitions -----------------
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ export default function Footer() {
|
|||
</Button>
|
||||
</a>
|
||||
<Label className={`font-normal text-xs text-gray-500`}>
|
||||
Elato AI © {new Date().getFullYear()} All rights
|
||||
reserved.
|
||||
Made with ❤️ by Elato AI © {new Date().getFullYear()}
|
||||
</Label>
|
||||
</div>
|
||||
{/* <Separator orientation="vertical" /> */}
|
||||
|
|
@ -77,20 +76,6 @@ export default function Footer() {
|
|||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="/privacy"
|
||||
className={`font-normal underline text-gray-500 text-xs`}
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/terms"
|
||||
className={`font-normal underline text-gray-500 text-xs`}
|
||||
>
|
||||
Terms of Service
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* <ThemeSwitcher /> */}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ export default async function LandingPage() {
|
|||
<Image src="/logos/arduino.png" alt="Arduino" width={100} height={24} style={{ height: '36px', width: 'auto' }} />
|
||||
</a>
|
||||
<a href="https://espressif.com" target="_blank" rel="noopener noreferrer" className="transition-all">
|
||||
<Image src="/logos/espressif.png" alt="Espressif ESP32" width={100} height={24} style={{ height: '24px', width: 'auto' }} />
|
||||
<Image src="/logos/espressif.png" alt="Espressif ESP32" width={100} height={24} style={{ height: '36px', width: 'auto' }} />
|
||||
</a>
|
||||
<a href="https://platformio.org" target="_blank" rel="noopener noreferrer" className="transition-all">
|
||||
<Image src="/logos/platformio.png" alt="PlatformIO" width={100} height={24} style={{ height: '36px', width: 'auto' }} />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Supabase keys
|
||||
SUPABASE_URL=<SUPABASE_URL>
|
||||
SUPABASE_KEY=<SUPABASE_KEY>
|
||||
JWT_SECRET=<JWT_SECRET>
|
||||
JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long
|
||||
|
||||
# Encryption Key (useful for encrypting secrets in the database)
|
||||
ENCRYPTION_KEY=<ENCRYPTION_KEY>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,27 @@
|
|||
import * as jose from 'https://deno.land/x/jose@v5.9.6/index.ts';
|
||||
import { getUserByEmail } from './supabase.ts';
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
import crypto from 'node:crypto';
|
||||
import { Buffer } from 'node:buffer';
|
||||
import * as jose from "https://deno.land/x/jose@v5.9.6/index.ts";
|
||||
import { getUserByEmail } from "./supabase.ts";
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
import crypto from "node:crypto";
|
||||
import { Buffer } from "node:buffer";
|
||||
|
||||
export const authenticateUser = async (
|
||||
supabaseClient: SupabaseClient,
|
||||
authToken: string,
|
||||
): Promise<IUser> => {
|
||||
try {
|
||||
const jwtSecret = Deno.env.get('JWT_SECRET');
|
||||
const jwtSecret = Deno.env.get("JWT_SECRET");
|
||||
console.log("jwtSecret", jwtSecret);
|
||||
|
||||
if (!jwtSecret) throw new Error('JWT_SECRET not configured');
|
||||
if (!jwtSecret) throw new Error("JWT_SECRET not configured");
|
||||
const secretBytes = new TextEncoder().encode(jwtSecret);
|
||||
const payload = await jose.jwtVerify(authToken, secretBytes);
|
||||
|
||||
const { payload: { email } } = payload;
|
||||
const user = await getUserByEmail(supabaseClient, email as string);
|
||||
console.log("user", user);
|
||||
return user;
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message || 'Failed to authenticate user');
|
||||
throw new Error(error.message || "Failed to authenticate user");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -30,20 +32,26 @@ export const authenticateUser = async (
|
|||
* @param masterKey - 32-byte string or buffer
|
||||
* @returns the original plaintext secret
|
||||
*/
|
||||
export function decryptSecret(encryptedData: string, iv: string, masterKey: string) {
|
||||
export function decryptSecret(
|
||||
encryptedData: string,
|
||||
iv: string,
|
||||
masterKey: string,
|
||||
) {
|
||||
// Decode the base64 master key
|
||||
const decodedKey = Buffer.from(masterKey, 'base64');
|
||||
const decodedKey = Buffer.from(masterKey, "base64");
|
||||
if (decodedKey.length !== 32) {
|
||||
throw new Error('ENCRYPTION_KEY must be 32 bytes when decoded from base64.');
|
||||
throw new Error(
|
||||
"ENCRYPTION_KEY must be 32 bytes when decoded from base64.",
|
||||
);
|
||||
}
|
||||
|
||||
const decipher = crypto.createDecipheriv(
|
||||
'aes-256-cbc',
|
||||
"aes-256-cbc",
|
||||
decodedKey, // Use the decoded key instead of raw masterKey
|
||||
Buffer.from(iv, 'base64'),
|
||||
Buffer.from(iv, "base64"),
|
||||
);
|
||||
|
||||
let decrypted = decipher.update(encryptedData, 'base64', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
let decrypted = decipher.update(encryptedData, "base64", "utf8");
|
||||
decrypted += decipher.final("utf8");
|
||||
return decrypted;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue