Skip to content

Commit

Permalink
User session bug fixes plus initial prometeus instrumentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
RickCarlino committed May 6, 2023
1 parent 9fd2849 commit 64231d9
Show file tree
Hide file tree
Showing 7 changed files with 706 additions and 318 deletions.
12 changes: 10 additions & 2 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
}
async rewrites() {
return [
{
source: "/metrics",
destination: "/api/prometheus",
},
];
},
};

module.exports = nextConfig
module.exports = nextConfig;
929 changes: 633 additions & 296 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"next-auth": "^4.22.1",
"nodemailer": "^6.9.1",
"openai": "^3.1.0",
"prom-client": "^14.2.0",
"radash": "^10.7.0",
"react": "18.2.0",
"react-dom": "18.2.0",
Expand Down
36 changes: 36 additions & 0 deletions pages/api/prometheus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { NextApiResponse } from "next";
import {
Counter,
CounterConfiguration,
collectDefaultMetrics,
register,
} from "prom-client";

const getOrCreateCounter = <T extends string>(
i: CounterConfiguration<T>
): Counter<T> => {
const existingMetric = register.getSingleMetric("seconds_spoken_total");

if (existingMetric) {
return existingMetric as Counter<T>;
}

return new Counter(i);
};

// Create a custom counter metric for counting HTTP requests
export const minutesSpoken = getOrCreateCounter({
name: "seconds_spoken_total",
help: "Total number of seconds that a user has spoken.",
labelNames: ["user_id"],
});
if (!(global as any).defaultMetricsInitialized) {
collectDefaultMetrics();
(global as any).defaultMetricsInitialized = true;
}

// Export a middleware function to expose a /metrics endpoint
export default async function (_: unknown, res: NextApiResponse) {
res.setHeader("Content-Type", register.contentType);
res.end(await register.metrics());
}
39 changes: 22 additions & 17 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { RecordButton } from "@/components/record-button";
import { trpc } from "@/utils/trpc";
import { Button, Grid } from "@mantine/core";
import * as React from "react";
import { useSession, signIn, signOut } from "next-auth/react"
import { useSession, signIn, signOut } from "next-auth/react";

type Mutation = ReturnType<typeof trpc.speak.useMutation>["mutateAsync"];
type Speech = Parameters<Mutation>[0]["text"];
type Phrase = NonNullable<
Expand Down Expand Up @@ -53,7 +54,7 @@ const Recorder: React.FC = () => {
const failSound = () => playAudio("/sfx/beep.mp3");
const okSound = () => playAudio("/sfx/tada.mp3");
const errorSound = () => playAudio("/sfx/flip.wav");
const { data: session } = useSession();
const { status } = useSession();

const doSetPhrase = async () => {
const p = await getPhrase.mutateAsync({});
Expand All @@ -63,18 +64,19 @@ const Recorder: React.FC = () => {
};

React.useEffect(() => {
!phrase && doSetPhrase();
}, []);
status === "authenticated" && !phrase && doSetPhrase();
}, [status]);

if (!session) {
return <div>
You need to login.
<Button onClick={() => signIn()}>Login</Button>
</div>
if (status === "unauthenticated") {
return <Button onClick={() => signIn()}>🔑Login</Button>;
}

if (status === "loading") {
return <Button>🌀Authenticating...</Button>;
}

if (!phrase) {
return <div>Loading Phrase</div>;
return <Button>📖Loading Phrase</Button>;
}

const sendAudio = async (audio: string) => {
Expand All @@ -96,22 +98,25 @@ const Recorder: React.FC = () => {
};

if (!dataURI) {
return <div>Loading Audio</div>;
return <Button>🔊Loading Audio</Button>;
}

return (
<Grid grow justify="center" align="center">
<Grid.Col span={3}>
<Grid.Col span={2}>
<Button onClick={() => signOut()}>🚪Sign Out</Button>
</Grid.Col>
<Grid.Col span={2}>
<Button onClick={() => alert("TODO")}>🚩Flag Item #{phrase.id}</Button>
</Grid.Col>
<Grid.Col span={3}>
<Grid.Col span={2}>
<PlayButton dataURI={dataURI} />
</Grid.Col>
<Grid.Col span={3}>
<Grid.Col span={2}>
<RecordButton quizType={phrase.quizType} onRecord={sendAudio} />
</Grid.Col>
<Grid.Col span={9}>
<Button onClick={() => signOut()}>Sign Out</Button>
</Grid.Col>
<Grid.Col span={2}></Grid.Col>
<Grid.Col span={2}></Grid.Col>
</Grid>
);
};
Expand Down
5 changes: 2 additions & 3 deletions server/trpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ export const procedure = t.procedure.use(
t.middleware(async ({ ctx, next }) => {
if (!ctx.session) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'Please log in.',
code: "UNAUTHORIZED",
message: "Please log in.",
});
}

return next();
})
);
2 changes: 2 additions & 0 deletions utils/transcribe.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { minutesSpoken } from "@/pages/api/prometheus";
import { SpeechClient } from "@google-cloud/speech";

export type Lang = "ko" | "en-US";
Expand Down Expand Up @@ -32,6 +33,7 @@ export async function transcribeB64(
content: captureAudio(dataURI),
},
});
minutesSpoken.inc({ user_id: "TBD" }, Number(resp.totalBilledTime?.seconds || 0));
const speech = resp?.results?.[0]?.alternatives?.[0]?.transcript;
if (!speech) {
return resolve({ kind: "NO_SPEECH" });
Expand Down

0 comments on commit 64231d9

Please sign in to comment.