Skip to content

Commit

Permalink
supabase auth implementation w/ rough edges in UI/X
Browse files Browse the repository at this point in the history
  • Loading branch information
martypdx committed Nov 2, 2023
1 parent 719bc77 commit b733678
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 42 deletions.
4 changes: 0 additions & 4 deletions supabase/config.toml
Expand Up @@ -132,7 +132,3 @@ port = 54327
vector_port = 54328
# Configure one of the supported backends: `postgres`, `bigquery`.
backend = "postgres"

[functions]
greeting.verify_jwt = false
invocation.verify_jwt = false
37 changes: 28 additions & 9 deletions www/index.html
Expand Up @@ -53,30 +53,49 @@
<!-- styles -->
<link rel="stylesheet" href="/styles/global.css" />
<link rel="stylesheet" href="/styles/form.css" />
<!-- js -->
<!-- <script defer referrerpolicy="origin" crossorigin="anonymous"
src="https://unpkg.com/typewriter-effect@latest/dist/core.js"></script>
<script defer referrerpolicy="origin" crossorigin="anonymous"
src='https://storage.ko-fi.com/cdn/scripts/overlay-widget.js'></script> -->

<!-- js -->
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
<script type="module" src="/src/app.js"></script>

</head>

<body>
<header class="page-header">
<!-- <div class="logo-wrapper">
<img width="23" height="23" class="logo-image" src="assets/spiritwave-avatar.png"
alt="Ghost in the Machine logo of white ghost looking right">
</div> -->
<p class="company-name">SpiritWave AI</p>
<button id="sign-out-button" class="hidden">sign out</button>
</header>

<main>
<section class="banner">
<img src="assets/spiritwave-beach.png"
alt="Illustration of a beach under a starlit sky. In the foreground, the selkie shaman with seal-like features stands near the water's edge, surrounded by glowing sea foam. Their tribal tattoos and luminescent staff illuminate the surroundings. The background blends the beach setting with the ethereal realms of the lower world, showing floating islands, luminescent marine life, and shimmering pathways connecting different dimensions. Spirit animals accompany the selkie, guiding them through this mystical journey.">
</section>

<section id="auth-section" class="hidden">
<form id="auth-form">
<label>
Email
<input name="email" type="email" placeholder="you@you.com" required>
</label>
<label>
Password
<input name="password" type="password" placeholder="ab$g76T" required>
</label>
<label>
<button name="sign-in">Sign in</button>
<button name="sign-up">Create account</button>
</label>
<p id="auth-error" class="error"></p>
</form>
</section>

<section id="session-section" class="hidden">
<p>
<button id="start-button">Let's Get Started</button>
</p>
</section>

<section id="output"></section>
</main>

Expand Down
43 changes: 42 additions & 1 deletion www/src/app.js
@@ -1 +1,42 @@
import './session.js';
import './services/auth.js';
import { signIn, signOut, signUp, watchAuth } from './services/auth.js';
import { startSession } from './session.js';

const authSection = document.getElementById('auth-section');
const sessionSection = document.getElementById('session-section');
const hide = element => element.classList.add('hidden');
const show = element => element.classList.remove('hidden');
const authMode = () => {
show(authSection);
hide(sessionSection);
hide(signOutButton);
};
const sessionMode = () => {
hide(authSection);
show(sessionSection);
show(signOutButton);
};

const authForm = document.getElementById('auth-form');
const authError = document.getElementById('auth-error');
authForm.addEventListener('submit', async e => {
e.preventDefault();
const action = e.submitter.name === 'sign-up' ? signUp : signIn;

const formData = new FormData(authForm);
const credentials = Object.fromEntries(formData.entries());
const { error } = await action(credentials);
if (error) {
authError.textContent = error?.message ?? error ?? 'Unexpected error';
}
});

const signOutButton = document.getElementById('sign-out-button');
signOutButton.addEventListener('click', signOut);

const startButton = document.getElementById('start-button');
startButton.addEventListener('click', startSession);

watchAuth((_event, session) => {
session ? sessionMode() : authMode();
});
22 changes: 22 additions & 0 deletions www/src/services/auth.js
@@ -0,0 +1,22 @@
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm';

const SUPABASE_PROJECT_URL = 'http://localhost:54321';
const SUPABASE_API_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0';

export const client = createClient(SUPABASE_PROJECT_URL, SUPABASE_API_KEY);

export function watchAuth(callback) {
client.auth.onAuthStateChange(callback);
}

export async function signUp(credentials){
return await client.auth.signUp(credentials);
}

export async function signIn(credentials){
return await client.auth.signInWithPassword(credentials);
}

export async function signOut(){
return await client.auth.signOut();
}
13 changes: 9 additions & 4 deletions www/src/services/spirit-wave.js
@@ -1,6 +1,14 @@
import { watchAuth } from './auth.js';

// Tests for this regex: https://regex101.com/r/5zXb2v/2
const ExtractPreContentRegex = /<pre>(.+?)<\/pre>/gims;

let token = null;

watchAuth((event, session) => {
token = session.access_token;
});

const getStream = (url) => async () => {
const res = await getResponse(url);
try {
Expand Down Expand Up @@ -62,10 +70,7 @@ function getResponse(url) {
try {
return fetch(url, {
headers: {
/* spell-checker: disable */
// TODO: use logged in supabase user
// Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0'
/* spell-checker: enable */
Authorization: `Bearer ${token}`
}
});
}
Expand Down
51 changes: 27 additions & 24 deletions www/src/session.js
@@ -1,33 +1,36 @@
import { streamGreeting, streamInvocation } from './services/spirit-wave.js';
import { streamGreeting } from './services/spirit-wave.js';
import { htmlToDomStream } from './streams.js';

const output = document.getElementById('output');

await tryStream(streamGreeting);
await injectContinue();
await tryStream(streamInvocation);
await injectContinue();
const done = document.createElement('p');
done.textContent = 'all done';
output.append(done);

async function injectContinue() {
const p = document.createElement('p');
const button = document.createElement('button');
button.textContent = 'continue...';
p.append(button);
output.append(p);
p.scrollIntoView({
behavior: 'smooth',
block: 'end',
});
export async function startSession() {

await tryStream(streamGreeting);
await injectContinue();
const done = document.createElement('p');
done.textContent = 'all done';
output.append(done);

async function injectContinue() {
const p = document.createElement('p');
const button = document.createElement('button');
button.textContent = 'continue...';
p.append(button);
output.append(p);
p.scrollIntoView({
behavior: 'smooth',
block: 'end',
});

return new Promise(resolve => {
button.addEventListener('click', async () => {
p.remove();
resolve();
return new Promise(resolve => {
button.addEventListener('click', async () => {
p.remove();
resolve();
});
});
});
}


}

async function tryStream(getStream) {
Expand Down
13 changes: 13 additions & 0 deletions www/styles/form.css
@@ -1,3 +1,8 @@
form {
display: grid;
grid-gap: 10px;

}

form input {
font-size: calc(1rem + ( var(--scale) * 3));
Expand All @@ -21,4 +26,12 @@ form button {
background: var(--lead-color);
border: 0;
color: white;
}

form p {
margin: 0;
}

.error {
color: var(--error-color);
}
5 changes: 5 additions & 0 deletions www/styles/global.css
Expand Up @@ -5,6 +5,7 @@
--lead-color: rgb(95 95 107);
--placeholder-color: rgb(136, 136, 153);
--action-color: rgb(59, 120, 244);
--error-color: rgb(197, 40, 40);
}

:root {
Expand Down Expand Up @@ -305,4 +306,8 @@ li {

pre {
word-wrap: break-word; white-space: pre-wrap;
}

.hidden {
display: none;
}

0 comments on commit b733678

Please sign in to comment.