mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 16:55:37 +02:00
⭐ feat(www): Add logic to the homepage and Steam integration (#258)
## Description <!-- Briefly describe the purpose and scope of your changes --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Upgraded API and authentication services with dynamic scaling, enhanced load balancing, and real-time interaction endpoints. - Introduced new commands to streamline local development and container builds. - Added new endpoints for retrieving Steam account information and managing connections. - Implemented a QR code authentication interface for Steam, enhancing user login experiences. - **Database Updates** - Rolled out comprehensive schema migrations that improve data integrity and indexing. - Introduced new tables for managing Steam user credentials and machine information. - **UI Enhancements** - Added refreshed animated assets and an improved QR code login flow for a more engaging experience. - Introduced new styled components for displaying friends and games. - **Maintenance** - Completed extensive refactoring and configuration updates to optimize performance and development workflows. - Updated logging configurations and improved error handling mechanisms. - Streamlined resource definitions in the configuration files. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -9,7 +9,7 @@ import { Examples } from "@nestri/core/examples";
|
||||
import { ErrorResponses, Result } from "./common";
|
||||
import { ErrorCodes, VisibleError } from "@nestri/core/error";
|
||||
|
||||
export module AccountApi {
|
||||
export namespace AccountApi {
|
||||
export const route = new Hono()
|
||||
.use(notPublic)
|
||||
.get("/",
|
||||
@@ -34,7 +34,8 @@ export module AccountApi {
|
||||
},
|
||||
description: "User account details"
|
||||
},
|
||||
404: ErrorResponses[404]
|
||||
404: ErrorResponses[404],
|
||||
429: ErrorResponses[429]
|
||||
}
|
||||
}),
|
||||
async (c) => {
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { Resource } from "sst";
|
||||
import { subjects } from "../subjects";
|
||||
import { type MiddlewareHandler } from "hono";
|
||||
import { VisibleError } from "@nestri/core/error";
|
||||
import { ActorContext } from "@nestri/core/actor";
|
||||
import { HTTPException } from "hono/http-exception";
|
||||
import { useActor, withActor } from "@nestri/core/actor";
|
||||
import { createClient } from "@openauthjs/openauth/client";
|
||||
import { ErrorCodes, VisibleError } from "@nestri/core/error";
|
||||
|
||||
const client = createClient({
|
||||
issuer: Resource.Auth.url,
|
||||
clientID: "api",
|
||||
issuer: Resource.Urls.auth
|
||||
});
|
||||
|
||||
|
||||
|
||||
export const notPublic: MiddlewareHandler = async (c, next) => {
|
||||
const actor = useActor();
|
||||
if (actor.type === "public")
|
||||
@@ -29,7 +24,7 @@ export const notPublic: MiddlewareHandler = async (c, next) => {
|
||||
export const auth: MiddlewareHandler = async (c, next) => {
|
||||
const authHeader =
|
||||
c.req.query("authorization") ?? c.req.header("authorization");
|
||||
if (!authHeader) return next();
|
||||
if (!authHeader) return withActor({ type: "public", properties: {} }, next);
|
||||
const match = authHeader.match(/^Bearer (.+)$/);
|
||||
if (!match) {
|
||||
throw new VisibleError(
|
||||
@@ -53,34 +48,22 @@ export const auth: MiddlewareHandler = async (c, next) => {
|
||||
return withActor(result.subject, next);
|
||||
}
|
||||
|
||||
if (result.subject.type === "user") {
|
||||
const teamID = c.req.header("x-nestri-team") //|| c.req.query("teamID");
|
||||
if (!teamID) return withActor(result.subject, next);
|
||||
// const email = result.subject.properties.email;
|
||||
return withActor(
|
||||
{
|
||||
type: "system",
|
||||
properties: {
|
||||
teamID,
|
||||
},
|
||||
if (result.subject.type === "user") {
|
||||
const teamID = c.req.header("x-nestri-team");
|
||||
if (!teamID) return withActor(result.subject, next);
|
||||
return withActor(
|
||||
{
|
||||
type: "system",
|
||||
properties: {
|
||||
teamID,
|
||||
},
|
||||
},
|
||||
next
|
||||
// async () => {
|
||||
// const user = await User.fromEmail(email);
|
||||
// if (!user || user.length === 0) {
|
||||
// c.status(401);
|
||||
// return c.text("Unauthorized");
|
||||
// }
|
||||
// return withActor(
|
||||
// {
|
||||
// type: "member",
|
||||
// properties: { userID: user[0].id, workspaceID: user.workspaceID },
|
||||
// },
|
||||
// next,
|
||||
// );
|
||||
// },
|
||||
async () => {
|
||||
return withActor(
|
||||
result.subject,
|
||||
next,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
return ActorContext.with({ type: "public", properties: {} }, next);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import {type Hook } from "./hook";
|
||||
import { z, ZodSchema } from "zod";
|
||||
import {type Hook } from "./types/hook";
|
||||
import { ErrorCodes, ErrorResponse } from "@nestri/core/error";
|
||||
import type { MiddlewareHandler, ValidationTargets } from "hono";
|
||||
import { resolver, validator as zodValidator } from "hono-openapi/zod";
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import "zod-openapi/extend";
|
||||
import { Hono } from "hono";
|
||||
import { auth } from "./auth";
|
||||
import { cors } from "hono/cors";
|
||||
import { TeamApi } from "./team";
|
||||
import { SteamApi } from "./steam";
|
||||
import { logger } from "hono/logger";
|
||||
import { Realtime } from "./realtime";
|
||||
import { AccountApi } from "./account";
|
||||
import { MachineApi } from "./machine";
|
||||
import { openAPISpecs } from "hono-openapi";
|
||||
import { patchLogger } from "../log-polyfill";
|
||||
import { HTTPException } from "hono/http-exception";
|
||||
import { handle, streamHandle } from "hono/aws-lambda";
|
||||
import { ErrorCodes, VisibleError } from "@nestri/core/error";
|
||||
|
||||
|
||||
export const app = new Hono();
|
||||
app
|
||||
.use(logger(), async (c, next) => {
|
||||
.use(logger())
|
||||
.use(cors())
|
||||
.use(async (c, next) => {
|
||||
c.header("Cache-Control", "no-store");
|
||||
return next();
|
||||
})
|
||||
@@ -21,11 +25,12 @@ app
|
||||
|
||||
const routes = app
|
||||
.get("/", (c) => c.text("Hello World!"))
|
||||
.route("/realtime", Realtime.route)
|
||||
.route("/team", TeamApi.route)
|
||||
.route("/steam", SteamApi.route)
|
||||
.route("/account", AccountApi.route)
|
||||
.route("/machine", MachineApi.route)
|
||||
.onError((error, c) => {
|
||||
console.warn(error);
|
||||
if (error instanceof VisibleError) {
|
||||
console.error("api error:", error);
|
||||
// @ts-expect-error
|
||||
@@ -54,7 +59,6 @@ const routes = app
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
app.get(
|
||||
"/doc",
|
||||
openAPISpecs(routes, {
|
||||
@@ -82,10 +86,21 @@ app.get(
|
||||
security: [{ Bearer: [], TeamID: [] }],
|
||||
servers: [
|
||||
{ description: "Production", url: "https://api.nestri.io" },
|
||||
{ description: "Sandbox", url: "https://api.dev.nestri.io" },
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
export type Routes = typeof routes;
|
||||
export const handler = process.env.SST_DEV ? handle(app) : streamHandle(app);
|
||||
patchLogger();
|
||||
|
||||
export default {
|
||||
port: 3001,
|
||||
idleTimeout: 255,
|
||||
webSocketHandler: Realtime.webSocketHandler,
|
||||
fetch: (req: Request) =>
|
||||
app.fetch(req, undefined, {
|
||||
waitUntil: (fn) => fn,
|
||||
passThroughOnException: () => { },
|
||||
}),
|
||||
};
|
||||
@@ -1,16 +1,92 @@
|
||||
import {z} from "zod"
|
||||
import {Hono} from "hono";
|
||||
import {notPublic} from "./auth";
|
||||
import {Result} from "../common";
|
||||
import {describeRoute} from "hono-openapi";
|
||||
import {assertActor} from "@nestri/core/actor";
|
||||
import {Realtime} from "@nestri/core/realtime/index";
|
||||
import {validator} from "hono-openapi/zod";
|
||||
import {CreateMessageSchema, StartMessageSchema, StopMessageSchema} from "./messages.ts";
|
||||
import { z } from "zod"
|
||||
import { Hono } from "hono";
|
||||
import { notPublic } from "./auth";
|
||||
import { describeRoute } from "hono-openapi";
|
||||
import { validator } from "hono-openapi/zod";
|
||||
import { Examples } from "@nestri/core/examples";
|
||||
import { assertActor } from "@nestri/core/actor";
|
||||
import { ErrorResponses, Result } from "./common";
|
||||
import { Machine } from "@nestri/core/machine/index";
|
||||
import { Realtime } from "@nestri/core/realtime/index";
|
||||
import { ErrorCodes, VisibleError } from "@nestri/core/error";
|
||||
import { CreateMessageSchema, StartMessageSchema, StopMessageSchema } from "./messages.ts";
|
||||
|
||||
export module MachineApi {
|
||||
export namespace MachineApi {
|
||||
export const route = new Hono()
|
||||
.use(notPublic)
|
||||
.get("/",
|
||||
describeRoute({
|
||||
tags: ["Machine"],
|
||||
summary: "Get all BYOG machines",
|
||||
description: "All the BYOG machines owned by this user",
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: Result(
|
||||
Machine.Info.array().openapi({
|
||||
description: "All the user's BYOG machines",
|
||||
example: [Examples.Machine],
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
description: "Successfully retrieved all the user's machines",
|
||||
},
|
||||
404: ErrorResponses[404],
|
||||
429: ErrorResponses[429]
|
||||
}
|
||||
}),
|
||||
async (c) => {
|
||||
const user = assertActor("user");
|
||||
const machineInfo = await Machine.fromUserID(user.properties.userID);
|
||||
|
||||
if (!machineInfo)
|
||||
throw new VisibleError(
|
||||
"not_found",
|
||||
ErrorCodes.NotFound.RESOURCE_NOT_FOUND,
|
||||
"No machines not found",
|
||||
);
|
||||
|
||||
return c.json({ data: machineInfo, }, 200);
|
||||
|
||||
})
|
||||
.get("/hosted",
|
||||
describeRoute({
|
||||
tags: ["Machine"],
|
||||
summary: "Get all cloud machines",
|
||||
description: "All the machines that are connected to Nestri",
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: Result(
|
||||
Machine.Info.array().openapi({
|
||||
description: "All the machines connected to Nestri",
|
||||
example: [{ ...Examples.Machine, userID: null }],
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
description: "Successfully retrieved all the hosted machines",
|
||||
},
|
||||
404: ErrorResponses[404],
|
||||
429: ErrorResponses[429]
|
||||
}
|
||||
}),
|
||||
async (c) => {
|
||||
const machineInfo = await Machine.list();
|
||||
|
||||
if (!machineInfo)
|
||||
throw new VisibleError(
|
||||
"not_found",
|
||||
ErrorCodes.NotFound.RESOURCE_NOT_FOUND,
|
||||
"No machines not found",
|
||||
);
|
||||
|
||||
return c.json({ data: machineInfo, }, 200);
|
||||
|
||||
})
|
||||
.post("/",
|
||||
describeRoute({
|
||||
tags: ["Machine"],
|
||||
@@ -27,14 +103,6 @@ export module MachineApi {
|
||||
},
|
||||
description: "Successfully sent the message to Maitred"
|
||||
},
|
||||
// 404: {
|
||||
// content: {
|
||||
// "application/json": {
|
||||
// schema: resolver(z.object({ error: z.string() })),
|
||||
// },
|
||||
// },
|
||||
// description: "This account does not exist",
|
||||
// },
|
||||
}
|
||||
}),
|
||||
validator(
|
||||
@@ -74,7 +142,7 @@ export module MachineApi {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: Result(
|
||||
z.object({error: z.string()})
|
||||
z.object({ error: z.string() })
|
||||
),
|
||||
},
|
||||
},
|
||||
@@ -97,7 +165,7 @@ export module MachineApi {
|
||||
console.log("Published create request to");
|
||||
} catch (error) {
|
||||
console.error("Failed to publish to MQTT:", error);
|
||||
return c.json({error: "Failed to send create request"}, 400);
|
||||
return c.json({ error: "Failed to send create request" }, 400);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
@@ -129,7 +197,7 @@ export module MachineApi {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: Result(
|
||||
z.object({error: z.string()})
|
||||
z.object({ error: z.string() })
|
||||
),
|
||||
},
|
||||
},
|
||||
@@ -154,7 +222,7 @@ export module MachineApi {
|
||||
console.log("Published start request");
|
||||
} catch (error) {
|
||||
console.error("Failed to publish to MQTT:", error);
|
||||
return c.json({error: "Failed to send start request"}, 400);
|
||||
return c.json({ error: "Failed to send start request" }, 400);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
@@ -186,7 +254,7 @@ export module MachineApi {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: Result(
|
||||
z.object({error: z.string()})
|
||||
z.object({ error: z.string() })
|
||||
),
|
||||
},
|
||||
},
|
||||
@@ -211,7 +279,7 @@ export module MachineApi {
|
||||
console.log("Published stop request");
|
||||
} catch (error) {
|
||||
console.error("Failed to publish to MQTT:", error);
|
||||
return c.json({error: "Failed to send stop request"}, 400);
|
||||
return c.json({ error: "Failed to send stop request" }, 400);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
|
||||
28
packages/functions/src/api/realtime/actor-core.ts
Normal file
28
packages/functions/src/api/realtime/actor-core.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { actor } from "actor-core";
|
||||
|
||||
// Define a chat room actor
|
||||
const chatRoom = actor({
|
||||
// Initialize state when the actor is first created
|
||||
createState: () => ({
|
||||
messages: [] as any[],
|
||||
}),
|
||||
|
||||
// Define actions clients can call
|
||||
actions: {
|
||||
// Action to send a message
|
||||
sendMessage: (c, sender, text) => {
|
||||
// Update state
|
||||
c.state.messages.push({ sender, text });
|
||||
|
||||
// Broadcast to all connected clients
|
||||
c.broadcast("newMessage", { sender, text });
|
||||
},
|
||||
|
||||
// Action to get chat history
|
||||
getHistory: (c) => {
|
||||
return c.state.messages;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default chatRoom;
|
||||
15
packages/functions/src/api/realtime/index.ts
Normal file
15
packages/functions/src/api/realtime/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { setup } from "actor-core";
|
||||
import chatRoom from "./actor-core";
|
||||
import { createRouter } from "@actor-core/bun";
|
||||
|
||||
export namespace Realtime {
|
||||
const app = setup({
|
||||
actors: { chatRoom },
|
||||
basePath: "/realtime"
|
||||
});
|
||||
|
||||
const realtimeRouter = createRouter(app);
|
||||
|
||||
export const route = realtimeRouter.router;
|
||||
export const webSocketHandler = realtimeRouter.webSocketHandler;
|
||||
}
|
||||
47
packages/functions/src/api/steam.ts
Normal file
47
packages/functions/src/api/steam.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Hono } from "hono";
|
||||
import { ErrorResponses, Result } from "./common";
|
||||
import { Steam } from "@nestri/core/steam/index";
|
||||
import { describeRoute } from "hono-openapi";
|
||||
import { Examples } from "@nestri/core/examples";
|
||||
import { assertActor } from "@nestri/core/actor";
|
||||
import { ErrorCodes, VisibleError } from "@nestri/core/error";
|
||||
|
||||
export namespace SteamApi {
|
||||
export const route = new Hono()
|
||||
.get("/",
|
||||
describeRoute({
|
||||
tags: ["Steam"],
|
||||
summary: "Get Steam account information",
|
||||
description: "Get the user's Steam account information",
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: Result(
|
||||
Steam.Info.openapi({
|
||||
description: "The Steam account information",
|
||||
example: Examples.Steam,
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
description: "Successfully got the Steam account information",
|
||||
},
|
||||
404: ErrorResponses[404],
|
||||
429: ErrorResponses[429],
|
||||
}
|
||||
}),
|
||||
async (c) => {
|
||||
const actor = assertActor("user");
|
||||
const steamInfo = await Steam.fromUserID(actor.properties.userID);
|
||||
if (!steamInfo)
|
||||
throw new VisibleError(
|
||||
"not_found",
|
||||
ErrorCodes.NotFound.RESOURCE_NOT_FOUND,
|
||||
"Steam account information not found",
|
||||
);
|
||||
|
||||
return c.json({ data: steamInfo }, 200);
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { Member } from "@nestri/core/member/index";
|
||||
import { assertActor, withActor } from "@nestri/core/actor";
|
||||
import { ErrorResponses, Result, validator } from "./common";
|
||||
|
||||
export module TeamApi {
|
||||
export namespace TeamApi {
|
||||
export const route = new Hono()
|
||||
.use(notPublic)
|
||||
.get("/",
|
||||
|
||||
@@ -2,8 +2,8 @@ import { Resource } from "sst"
|
||||
import { Select } from "./ui/select";
|
||||
import { subjects } from "./subjects"
|
||||
import { logger } from "hono/logger";
|
||||
import { handle } from "hono/aws-lambda";
|
||||
import { PasswordUI } from "./ui/password"
|
||||
import { patchLogger } from "./log-polyfill";
|
||||
import { issuer } from "@openauthjs/openauth";
|
||||
import { User } from "@nestri/core/user/index"
|
||||
import { Email } from "@nestri/core/email/index";
|
||||
@@ -11,8 +11,9 @@ import { handleDiscord, handleGithub } from "./utils";
|
||||
import { GithubAdapter } from "./ui/adapters/github";
|
||||
import { Machine } from "@nestri/core/machine/index"
|
||||
import { DiscordAdapter } from "./ui/adapters/discord";
|
||||
import { PasswordAdapter } from "./ui/adapters/password"
|
||||
import { PasswordAdapter } from "./ui/adapters/password";
|
||||
import { type Provider } from "@openauthjs/openauth/provider/provider"
|
||||
import { MemoryStorage } from "@openauthjs/openauth/storage/memory";
|
||||
|
||||
type OauthUser = {
|
||||
primary: {
|
||||
@@ -24,13 +25,13 @@ type OauthUser = {
|
||||
username: any;
|
||||
}
|
||||
|
||||
console.log("STORAGE", process.env.STORAGE)
|
||||
|
||||
const app = issuer({
|
||||
select: Select({
|
||||
providers: {
|
||||
machine: {
|
||||
hide: true,
|
||||
},
|
||||
},
|
||||
select: Select(),
|
||||
//TODO: Create our own Storage
|
||||
storage: MemoryStorage({
|
||||
persist: process.env.STORAGE //"/tmp/persist.json",
|
||||
}),
|
||||
theme: {
|
||||
title: "Nestri | Auth",
|
||||
@@ -46,9 +47,7 @@ const app = issuer({
|
||||
font: {
|
||||
family: "Geist, sans-serif",
|
||||
},
|
||||
css: `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Geist:wght@100;200;300;400;500;600;700;800;900&display=swap');
|
||||
`,
|
||||
css: `@import url('https://fonts.googleapis.com/css2?family=Geist:wght@100;200;300;400;500;600;700;800;900&display=swap');`,
|
||||
},
|
||||
subjects,
|
||||
providers: {
|
||||
@@ -78,9 +77,10 @@ const app = issuer({
|
||||
machine: {
|
||||
type: "machine",
|
||||
async client(input) {
|
||||
if (input.clientSecret !== Resource.AuthFingerprintKey.value) {
|
||||
throw new Error("Invalid authorization token");
|
||||
}
|
||||
// FIXME: Do we really need this?
|
||||
// if (input.clientSecret !== Resource.AuthFingerprintKey.value) {
|
||||
// throw new Error("Invalid authorization token");
|
||||
// }
|
||||
|
||||
const fingerprint = input.params.fingerprint;
|
||||
if (!fingerprint) {
|
||||
@@ -120,7 +120,9 @@ const app = issuer({
|
||||
location: {
|
||||
latitude,
|
||||
longitude
|
||||
}
|
||||
},
|
||||
//FIXME: Make this better
|
||||
userID: null
|
||||
})
|
||||
return ctx.subject("machine", {
|
||||
machineID,
|
||||
@@ -134,7 +136,7 @@ const app = issuer({
|
||||
});
|
||||
}
|
||||
|
||||
//TODO: This works, so use this while registering the task
|
||||
// TODO: This works, so use this while registering the task
|
||||
// console.log("country_code", req.headers.get('CloudFront-Viewer-Country'))
|
||||
// console.log("country_name", req.headers.get('CloudFront-Viewer-Country-Name'))
|
||||
// console.log("latitude", req.headers.get('CloudFront-Viewer-Latitude'))
|
||||
@@ -225,4 +227,14 @@ const app = issuer({
|
||||
},
|
||||
}).use(logger())
|
||||
|
||||
export const handler = handle(app)
|
||||
patchLogger();
|
||||
|
||||
export default {
|
||||
port: 3002,
|
||||
idleTimeout: 255,
|
||||
fetch: (req: Request) =>
|
||||
app.fetch(req, undefined, {
|
||||
waitUntil: (fn) => fn,
|
||||
passThroughOnException: () => { },
|
||||
}),
|
||||
};
|
||||
27
packages/functions/src/log-polyfill.ts
Normal file
27
packages/functions/src/log-polyfill.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { format } from "util";
|
||||
|
||||
/**
|
||||
* Overrides the default Node.js console logging methods with a custom logger.
|
||||
*
|
||||
* This function patches console.log, console.warn, console.error, console.trace, and console.debug so that each logs
|
||||
* messages prefixed with a log level. The messages are formatted using Node.js formatting conventions, with newline
|
||||
* characters replaced by carriage returns, and are written directly to standard output.
|
||||
*
|
||||
* @example
|
||||
* patchLogger();
|
||||
* console.info("Server started on port %d", 3000);
|
||||
*/
|
||||
export function patchLogger() {
|
||||
const log =
|
||||
(level: "INFO" | "WARN" | "TRACE" | "DEBUG" | "ERROR") =>
|
||||
(msg: string, ...rest: any[]) => {
|
||||
let line = `${level}\t${format(msg, ...rest)}`;
|
||||
line = line.replace(/\n/g, "\r");
|
||||
process.stdout.write(line + "\n");
|
||||
};
|
||||
console.log = log("INFO");
|
||||
console.warn = log("WARN");
|
||||
console.error = log("ERROR");
|
||||
console.trace = log("TRACE");
|
||||
console.debug = log("DEBUG");
|
||||
}
|
||||
38
packages/functions/src/party/authorizer.ts
Normal file
38
packages/functions/src/party/authorizer.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Resource } from "sst";
|
||||
import { subjects } from "../subjects";
|
||||
import { realtime } from "sst/aws/realtime";
|
||||
import { createClient } from "@openauthjs/openauth/client";
|
||||
|
||||
export const handler = realtime.authorizer(async (token) => {
|
||||
//TODO: Use the following criteria for a topic - team-slug/container-id (container ids are not unique globally)
|
||||
//TODO: Allow the authorizer to subscriber/publisher to listen on - team-slug topics only (as the container will listen on the team-slug/container-id topic to be specific)
|
||||
// Return the topics to subscribe and publish
|
||||
|
||||
const client = createClient({
|
||||
clientID: "api",
|
||||
issuer: Resource.Auth.url
|
||||
});
|
||||
|
||||
const result = await client.verify(subjects, token);
|
||||
|
||||
if (result.err) {
|
||||
console.log("error", result.err)
|
||||
return {
|
||||
subscribe: [],
|
||||
publish: [],
|
||||
};
|
||||
}
|
||||
|
||||
if (result.subject.type != "user") {
|
||||
return {
|
||||
subscribe: [],
|
||||
publish: [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
//It can publish and listen to other instances under this team
|
||||
subscribe: [`${Resource.App.name}/${Resource.App.stage}/*`],
|
||||
publish: [`${Resource.App.name}/${Resource.App.stage}/*`],
|
||||
};
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import { createClient } from "@openauthjs/openauth/client";
|
||||
|
||||
const client = createClient({
|
||||
clientID: "realtime",
|
||||
issuer: Resource.Urls.auth
|
||||
issuer: Resource.Auth.url
|
||||
});
|
||||
|
||||
export const handler = realtime.authorizer(async (token) => {
|
||||
|
||||
Reference in New Issue
Block a user