feat: Add a lot of stuff

This commit is contained in:
Wanjohi
2025-10-25 22:11:33 +03:00
parent a3ee9aadd9
commit c5d0242d50
85 changed files with 6718 additions and 1377 deletions

View File

@@ -1,51 +1,51 @@
{
"name": "@nestri/core",
"version": "0.0.0",
"sideEffects": false,
"type": "module",
"scripts": {
"db:dev": "drizzle-kit",
"typecheck": "tsc --noEmit",
"db": "sst shell drizzle-kit",
"db:exec": "sst shell ../scripts/src/psql.sh",
"db:reset": "sst shell ../scripts/src/db-reset.sh"
},
"exports": {
"./*": "./src/*.ts"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/pngjs": "^6.0.5",
"@types/sanitize-html": "^2.16.0",
"@types/xml2js": "^0.4.14",
"aws-iot-device-sdk-v2": "^1.21.1",
"aws4fetch": "^1.0.20",
"mqtt": "^5.10.3",
"remeda": "^2.21.2",
"ulid": "^2.3.0",
"zod": "^3.24.1",
"zod-openapi": "^4.2.2"
},
"dependencies": {
"@aws-sdk/client-iot-data-plane": "^3.758.0",
"@aws-sdk/client-sesv2": "^3.753.0",
"@neondatabase/serverless": "^1.0.1",
"@openauthjs/openauth": "*",
"@openauthjs/openevent": "^0.0.27",
"@polar-sh/sdk": "^0.26.1",
"drizzle-kit": "^0.30.5",
"drizzle-orm": "^0.40.0",
"drizzle-zod": "^0.7.1",
"fast-average-color": "^9.5.0",
"lru-cache": "^11.1.0",
"p-limit": "^6.2.0",
"pixelmatch": "^7.1.0",
"pngjs": "^7.0.0",
"postgres": "^3.4.5",
"sanitize-html": "^2.16.0",
"sharp": "^0.34.1",
"steam-session": "*",
"ws": "^8.18.3",
"xml2js": "^0.6.2"
}
}
"name": "@nestri/cloud-core",
"version": "0.0.0",
"sideEffects": false,
"type": "module",
"scripts": {
"db:dev": "drizzle-kit",
"typecheck": "tsc --noEmit",
"db": "sst shell drizzle-kit",
"db:exec": "sst shell ../scripts/src/psql.sh",
"db:reset": "sst shell ../scripts/src/db-reset.sh"
},
"exports": {
"./*": "./src/*.ts"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/pngjs": "^6.0.5",
"@types/sanitize-html": "^2.16.0",
"@types/xml2js": "^0.4.14",
"aws-iot-device-sdk-v2": "^1.21.1",
"aws4fetch": "^1.0.20",
"mqtt": "^5.10.3",
"remeda": "^2.21.2",
"ulid": "^2.3.0",
"zod": "^3.24.1",
"zod-openapi": "^4.2.2"
},
"dependencies": {
"@aws-sdk/client-iot-data-plane": "^3.758.0",
"@aws-sdk/client-sesv2": "^3.753.0",
"@neondatabase/serverless": "^1.0.1",
"@openauthjs/openauth": "catalog:",
"@openauthjs/openevent": "^0.0.27",
"@polar-sh/sdk": "^0.26.1",
"drizzle-kit": "^0.30.5",
"drizzle-orm": "^0.40.0",
"drizzle-zod": "^0.7.1",
"fast-average-color": "^9.5.0",
"lru-cache": "^11.1.0",
"p-limit": "^6.2.0",
"pixelmatch": "^7.1.0",
"pngjs": "^7.0.0",
"postgres": "^3.4.5",
"sanitize-html": "^2.16.0",
"sharp": "^0.34.1",
"steam-session": "*",
"ws": "^8.18.3",
"xml2js": "^0.6.2"
}
}

View File

@@ -0,0 +1,97 @@
import { z } from "zod/v4";
import { fn } from "../utils/fn";
import { Examples } from "../examples.js";
import { createID } from "../utils/id.js";
import { Polar } from "../polar/index.js";
import { Database } from "../drizzle/index.js";
import { teamTable } from "../team/team.sql.js";
import { and, asc, eq, isNull } from "drizzle-orm";
import { VisibleError, ErrorCodes } from "../error.js";
export namespace Team {
export const Info = z
.object({
id: z.string().meta({
description: "The unique identifier of the team",
example: Examples.Team.id,
}),
email: z.email().meta({
description: "The contact email address for the team",
example: Examples.Team.email,
}),
polarCustomerID: z.string().nullable().meta({
description: "Associated Polar.sh customer identifier",
example: Examples.Team.polarCustomerID,
}),
})
.meta({
ref: "Team",
description: "Represents a team entity",
example: Examples.Team,
});
export type Info = z.infer<typeof Info>;
export class TeamExistsError extends VisibleError {
constructor(email: string) {
super(
"already_exists",
ErrorCodes.Validation.ALREADY_EXISTS,
`A team with this email ${email} already exists`,
);
}
}
export const create = fn(
Info.omit({ polarCustomerID: true }).partial({ id: true }),
async (input) => {
const userID = createID("team");
const customerID = await Polar.fromUserEmail(input.email).then(
(polarUser) => polarUser?.id || null,
);
const id = input.id ?? userID;
await Database.transaction(async (tx) => {
const result = await tx
.insert(teamTable)
.values({
id,
email: input.email,
polarCustomerID: customerID,
})
.onConflictDoNothing({
target: [teamTable.email],
});
if (result.count === 0) {
throw new TeamExistsError(input.email);
}
});
return id;
},
);
export const fromEmail = fn(Info.shape.email, async (email) =>
Database.transaction(async (tx) =>
tx
.select()
.from(teamTable)
.where(and(eq(teamTable.email, email), isNull(teamTable.timeDeleted)))
.orderBy(asc(teamTable.timeCreated))
.execute()
.then((rows) => rows.map(serialize).at(0)),
),
);
export function serialize(
input: typeof teamTable.$inferSelect,
): z.infer<typeof Info> {
return {
id: input.id,
email: input.email,
polarCustomerID: input.polarCustomerID,
};
}
}

View File

@@ -0,0 +1,9 @@
import { ulid, timestamps } from "@/drizzle/types.js";
import { pgTable, varchar } from "drizzle-orm/pg-core";
export const teamTable = pgTable("team", {
...timestamps,
id: ulid("id").primaryKey().notNull(),
email: varchar("email", { length: 255 }).unique().notNull(),
polarCustomerID: varchar("polar_customer_id", { length: 255 }),
});

View File

@@ -1,27 +1,30 @@
import { ZodSchema, z } from "zod";
import { ZodType, z } from "zod/v4";
export function fn<
Arg1 extends ZodSchema,
Callback extends (arg1: z.output<Arg1>) => any,
Arg1 extends ZodType,
Callback extends (arg1: z.output<Arg1>) => any,
>(arg1: Arg1, cb: Callback) {
const result = function (input: z.input<typeof arg1>): ReturnType<Callback> {
const parsed = arg1.parse(input);
return cb.apply(cb, [parsed as any]);
};
result.schema = arg1;
return result;
const result = function (input: z.input<typeof arg1>): ReturnType<Callback> {
const parsed = arg1.parse(input);
return cb.apply(cb, [parsed as any]);
};
result.schema = arg1;
return result;
}
export function doubleFn<
Arg1 extends ZodSchema,
Arg2 extends ZodSchema,
Callback extends (arg1: z.output<Arg1>, arg2: z.output<Arg2>) => any,
Arg1 extends ZodType,
Arg2 extends ZodType,
Callback extends (arg1: z.output<Arg1>, arg2: z.output<Arg2>) => any,
>(arg1: Arg1, arg2: Arg2, cb: Callback) {
const result = function (input: z.input<typeof arg1>, input2: z.input<typeof arg2>): ReturnType<Callback> {
const parsed = arg1.parse(input);
const parsed2 = arg2.parse(input2);
return cb.apply(cb, [parsed as any, parsed2 as any]);
};
result.schema = arg1;
return result;
}
const result = function (
input: z.input<typeof arg1>,
input2: z.input<typeof arg2>,
): ReturnType<Callback> {
const parsed = arg1.parse(input);
const parsed2 = arg2.parse(input2);
return cb.apply(cb, [parsed as any, parsed2 as any]);
};
result.schema = arg1;
return result;
}

View File

@@ -1,8 +1,11 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"strict": true,
"module": "esnext",
"moduleResolution": "bundler",
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"strict": true,
"module": "esnext",
"moduleResolution": "bundler",
"paths": {
"@/*": ["./src/*"]
}
}
}
}

View File

@@ -1,5 +1,5 @@
{
"name": "@nestri/functions",
"name": "@nestri/cloud-functions",
"module": "index.ts",
"type": "module",
"private": true,
@@ -22,6 +22,7 @@
"@aws-sdk/client-s3": "^3.806.0",
"@aws-sdk/client-sqs": "^3.806.0",
"@nestri/core": "workspace:",
"@nestri/cloud-core": "workspace:",
"actor-core": "^0.8.0",
"hono": "^4.7.8",
"hono-openapi": "^0.4.8",

View File

@@ -3,9 +3,10 @@ import { Resource } from "sst";
import { logger } from "hono/logger";
import { subjects } from "../subjects";
import { handleDiscord } from "./utils";
import { Team } from "@nestri/cloud-core/team/index";
import { DiscordAdapter } from "./adapters";
import { issuer } from "@openauthjs/openauth";
import { User } from "@nestri/core/user/index";
import { User } from "@nestri/cloud-core/user/index";
import { patchLogger } from "../utils/patch-logger";
import type { KVNamespace } from "@cloudflare/workers-types";
import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare";
@@ -63,6 +64,7 @@ export default {
if (user) {
try {
await Team.fromEmail(email);
const matching = await User.fromEmail(user.primary.email);
//Sign Up