feat: New account system with improved team management (#273)

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**
- Introduced comprehensive account management with combined user and
team info.
  - Added advanced, context-aware logging utilities.
- Implemented invite code generation for teams with uniqueness
guarantees.
- Expanded example data for users, teams, subscriptions, sessions, and
games.

- **Enhancements**
- Refined user, team, member, and Steam account schemas for richer data
and validation.
  - Streamlined user creation, login acknowledgment, and error handling.
  - Improved API authentication and unified actor context management.
- Added persistent shared temporary volume support to API and auth
services.
- Enhanced Steam account management with create, update, and event
notifications.
- Refined team listing and serialization integrating Steam accounts as
members.
  - Simplified event, context, and logging systems.
- Updated API and auth middleware for better token handling and actor
provisioning.

- **Bug Fixes**
  - Fixed multiline log output to prefix each line with log level.

- **Removals**
- Removed machine and subscription management features, including
schemas and DB tables.
- Disabled machine-based authentication and removed related subject
schemas.
- Removed deprecated fields and legacy logic from member and team
management.
- Removed legacy event and error handling related to teams and members.

- **Chores**
  - Reorganized and cleaned exports across utility and API modules.
- Updated database schemas for users, teams, members, and Steam
accounts.
  - Improved internal code structure, imports, and error messaging.
- Moved logger patching to earlier initialization for consistent
logging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Wanjohi
2025-05-06 07:26:59 +03:00
committed by GitHub
parent a0dc353561
commit 70d629227a
39 changed files with 1194 additions and 1480 deletions

View File

@@ -1,13 +1,9 @@
import { z } from "zod";
import { Hono } from "hono";
import { notPublic } from "./utils/auth";
import { notPublic } from "./utils";
import { describeRoute } from "hono-openapi";
import { User } from "@nestri/core/user/index";
import { Team } from "@nestri/core/team/index";
import { assertActor } from "@nestri/core/actor";
import { Examples } from "@nestri/core/examples";
import { ErrorResponses, Result } from "./utils";
import { ErrorCodes, VisibleError } from "@nestri/core/error";
import { Account } from "@nestri/core/account/index";
export namespace AccountApi {
export const route = new Hono()
@@ -22,10 +18,7 @@ export namespace AccountApi {
content: {
"application/json": {
schema: Result(
z.object({
...User.Info.shape,
teams: Team.Info.array(),
}).openapi({
Account.Info.openapi({
description: "User account information",
example: { ...Examples.User, teams: [Examples.Team] }
})
@@ -34,27 +27,14 @@ export namespace AccountApi {
},
description: "User account details"
},
400: ErrorResponses[400],
404: ErrorResponses[404],
429: ErrorResponses[429]
429: ErrorResponses[429],
}
}),
async (c) => {
const actor = assertActor("user");
const [currentUser, teams] = await Promise.all([User.fromID(actor.properties.userID), User.teams()])
if (!currentUser)
throw new VisibleError(
"not_found",
ErrorCodes.NotFound.RESOURCE_NOT_FOUND,
"User not found",
);
return c.json({
data: {
...currentUser,
teams,
}
}, 200);
},
async (c) =>
c.json({
data: await Account.list()
}, 200)
)
}

View File

@@ -9,6 +9,8 @@ import { patchLogger } from "../utils/patch-logger";
import { HTTPException } from "hono/http-exception";
import { ErrorCodes, VisibleError } from "@nestri/core/error";
patchLogger();
export const app = new Hono();
app
.use(logger())
@@ -85,8 +87,6 @@ app.get(
}),
);
patchLogger();
export default {
port: 3001,
idleTimeout: 255,

View File

@@ -1,7 +1,7 @@
import { Resource } from "sst";
import { subjects } from "../../subjects";
import { Actor } from "@nestri/core/actor";
import { type MiddlewareHandler } from "hono";
import { useActor, withActor } from "@nestri/core/actor";
import { createClient } from "@openauthjs/openauth/client";
import { ErrorCodes, VisibleError } from "@nestri/core/error";
@@ -11,7 +11,7 @@ const client = createClient({
});
export const notPublic: MiddlewareHandler = async (c, next) => {
const actor = useActor();
const actor = Actor.use();
if (actor.type === "public")
throw new VisibleError(
"authentication",
@@ -22,9 +22,8 @@ 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 withActor({ type: "public", properties: {} }, next);
const authHeader = c.req.header("authorization");
if (!authHeader) return Actor.provide("public", {}, next);
const match = authHeader.match(/^Bearer (.+)$/);
if (!match) {
throw new VisibleError(
@@ -44,20 +43,24 @@ export const auth: MiddlewareHandler = async (c, next) => {
}
if (result.subject.type === "user") {
const user = { ...result.subject.properties }
const teamID = c.req.header("x-nestri-team");
if (!teamID) return withActor(result.subject, next);
return withActor(
if (!teamID) {
return Actor.provide("user", {
...user
}, next);
}
return Actor.provide(
"system",
{
type: "system",
properties: {
teamID,
},
teamID
},
async () =>
withActor(
result.subject,
next,
)
Actor.provide("user", {
...user
}, next)
);
}
return Actor.provide("public", {}, next);
};

View File

@@ -1,3 +1,4 @@
export * from "./validator";
export * from "./auth";
export * from "./error";
export * from "./result";
export * from "./error";
export * from "./validator";