mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 08:45:38 +02:00
⭐ feat(maitred): Update maitred - hookup to the API (#198)
## Description We are attempting to hookup maitred to the API Maitred duties will be: - [ ] Hookup to the API - [ ] Wait for signal (from the API) to start Steam - [ ] Stop signal to stop the gaming session, clean up Steam... and maybe do the backup ## Summary by CodeRabbit - **New Features** - Introduced Docker-based deployment configurations for both the main and relay applications. - Added new API endpoints enabling real-time machine messaging and enhanced IoT operations. - Expanded database schema and actor types to support improved machine tracking. - **Improvements** - Enhanced real-time communication and relay management with streamlined room handling. - Upgraded dependencies, logging, and error handling for greater stability and performance. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com> Co-authored-by: Kristian Ollikainen <14197772+DatCaptainHorse@users.noreply.github.com>
This commit is contained in:
13
packages/core/migrations/0002_tiny_toad_men.sql
Normal file
13
packages/core/migrations/0002_tiny_toad_men.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE "machine" (
|
||||
"id" char(30) PRIMARY KEY NOT NULL,
|
||||
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"time_updated" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"time_deleted" timestamp with time zone,
|
||||
"country" text NOT NULL,
|
||||
"timezone" text NOT NULL,
|
||||
"location" "point" NOT NULL,
|
||||
"fingerprint" varchar(32) NOT NULL,
|
||||
"country_code" varchar(2) NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "machine_fingerprint" ON "machine" USING btree ("fingerprint");
|
||||
379
packages/core/migrations/meta/0002_snapshot.json
Normal file
379
packages/core/migrations/meta/0002_snapshot.json
Normal file
@@ -0,0 +1,379 @@
|
||||
{
|
||||
"id": "aa60489b-b4e2-4a69-aee7-16e050d02ef9",
|
||||
"prevId": "6f428226-b5d8-4182-a676-d04f842f9ded",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.machine": {
|
||||
"name": "machine",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "char(30)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"country": {
|
||||
"name": "country",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"timezone": {
|
||||
"name": "timezone",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"location": {
|
||||
"name": "location",
|
||||
"type": "point",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"fingerprint": {
|
||||
"name": "fingerprint",
|
||||
"type": "varchar(32)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"country_code": {
|
||||
"name": "country_code",
|
||||
"type": "varchar(2)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"machine_fingerprint": {
|
||||
"name": "machine_fingerprint",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "fingerprint",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.member": {
|
||||
"name": "member",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "char(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"team_id": {
|
||||
"name": "team_id",
|
||||
"type": "char(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"time_seen": {
|
||||
"name": "time_seen",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"email_global": {
|
||||
"name": "email_global",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
},
|
||||
"member_email": {
|
||||
"name": "member_email",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "team_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
},
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"member_team_id_id_pk": {
|
||||
"name": "member_team_id_id_pk",
|
||||
"columns": [
|
||||
"team_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.team": {
|
||||
"name": "team",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "char(30)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"plan_type": {
|
||||
"name": "plan_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "slug",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "char(30)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"avatar_url": {
|
||||
"name": "avatar_url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"discriminator": {
|
||||
"name": "discriminator",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"polar_customer_id": {
|
||||
"name": "polar_customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"flags": {
|
||||
"name": "flags",
|
||||
"type": "json",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'{}'::json"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"user_polar_customer_id_unique": {
|
||||
"name": "user_polar_customer_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"polar_customer_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,13 @@
|
||||
"when": 1741955636085,
|
||||
"tag": "0001_nifty_sauron",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "7",
|
||||
"when": 1743028682022,
|
||||
"tag": "0002_tiny_toad_men",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
"zod-openapi": "^4.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-iot-data-plane": "^3.758.0",
|
||||
"@aws-sdk/client-rds-data": "^3.758.0",
|
||||
"@aws-sdk/client-sesv2": "^3.753.0",
|
||||
"@instantdb/admin": "^0.17.7",
|
||||
|
||||
@@ -37,15 +37,25 @@ export const SystemActor = z.object({
|
||||
});
|
||||
export type SystemActor = z.infer<typeof SystemActor>;
|
||||
|
||||
export const MachineActor = z.object({
|
||||
type: z.literal("machine"),
|
||||
properties: z.object({
|
||||
fingerprint: z.string(),
|
||||
machineID: z.string(),
|
||||
}),
|
||||
});
|
||||
export type MachineActor = z.infer<typeof MachineActor>;
|
||||
|
||||
export const Actor = z.discriminatedUnion("type", [
|
||||
MemberActor,
|
||||
UserActor,
|
||||
PublicActor,
|
||||
SystemActor,
|
||||
MachineActor
|
||||
]);
|
||||
export type Actor = z.infer<typeof Actor>;
|
||||
|
||||
const ActorContext = createContext<Actor>("actor");
|
||||
export const ActorContext = createContext<Actor>("actor");
|
||||
|
||||
export const useActor = ActorContext.use;
|
||||
export const withActor = ActorContext.with;
|
||||
@@ -74,6 +84,12 @@ export function useTeam() {
|
||||
throw new Error(`Expected actor to have teamID`);
|
||||
}
|
||||
|
||||
export function useMachine() {
|
||||
const actor = useActor();
|
||||
if ("machineID" in actor.properties) return actor.properties.fingerprint;
|
||||
throw new Error(`Expected actor to have fingerprint`);
|
||||
}
|
||||
|
||||
export async function assertUserFlag(flag: keyof UserFlags) {
|
||||
return useTransaction((tx) =>
|
||||
tx
|
||||
|
||||
@@ -31,4 +31,13 @@ export module Examples {
|
||||
timeSeen: new Date("2025-02-23T13:39:52.249Z"),
|
||||
}
|
||||
|
||||
export const Machine = {
|
||||
id: Id("machine"),
|
||||
country: "Kenya",
|
||||
countryCode: "KE",
|
||||
timezone: "Africa/Nairobi",
|
||||
location: { latitude: 36.81550, longitude: -1.28410 },
|
||||
fingerprint: "fc27f428f9ca47d4b41b707ae0c62090",
|
||||
}
|
||||
|
||||
}
|
||||
138
packages/core/src/machine/index.ts
Normal file
138
packages/core/src/machine/index.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { z } from "zod";
|
||||
import { Common } from "../common";
|
||||
import { createID, fn } from "../utils";
|
||||
import { Examples } from "../examples";
|
||||
import { machineTable } from "./machine.sql";
|
||||
import { getTableColumns, eq, sql, and, isNull } from "../drizzle";
|
||||
import { createTransaction, useTransaction } from "../drizzle/transaction";
|
||||
|
||||
export module Machine {
|
||||
export const Info = z
|
||||
.object({
|
||||
id: z.string().openapi({
|
||||
description: Common.IdDescription,
|
||||
example: Examples.Machine.id,
|
||||
}),
|
||||
country: z.string().openapi({
|
||||
description: "The fullname of the country this machine is running in",
|
||||
example: Examples.Machine.country
|
||||
}),
|
||||
fingerprint: z.string().openapi({
|
||||
description: "The fingerprint of this machine, deduced from the host machine's machine id - /etc/machine-id",
|
||||
example: Examples.Machine.fingerprint
|
||||
}),
|
||||
location: z.object({ longitude: z.number(), latitude: z.number() }).openapi({
|
||||
description: "This is the 2d location of this machine, they might not be accurate",
|
||||
example: Examples.Machine.location
|
||||
}),
|
||||
countryCode: z.string().openapi({
|
||||
description: "This is the 2 character country code of the country this machine [ISO 3166-1 alpha-2] ",
|
||||
example: Examples.Machine.countryCode
|
||||
}),
|
||||
timezone: z.string().openapi({
|
||||
description: "The IANA timezone formatted string of the timezone of the location where the machine is running",
|
||||
example: Examples.Machine.timezone
|
||||
})
|
||||
})
|
||||
.openapi({
|
||||
ref: "Machine",
|
||||
description: "Represents a hosted or BYOG machine connected to Nestri",
|
||||
example: Examples.Machine,
|
||||
});
|
||||
|
||||
export type Info = z.infer<typeof Info>;
|
||||
|
||||
export const create = fn(Info.partial({ id: true }), async (input) =>
|
||||
createTransaction(async (tx) => {
|
||||
const id = input.id ?? createID("machine");
|
||||
await tx.insert(machineTable).values({
|
||||
id,
|
||||
country: input.country,
|
||||
timezone: input.timezone,
|
||||
fingerprint: input.fingerprint,
|
||||
countryCode: input.countryCode,
|
||||
location: { x: input.location.longitude, y: input.location.latitude },
|
||||
})
|
||||
|
||||
// await afterTx(() =>
|
||||
// bus.publish(Resource.Bus, Events.Created, {
|
||||
// teamID: id,
|
||||
// }),
|
||||
// );
|
||||
return id;
|
||||
})
|
||||
)
|
||||
|
||||
export const list = fn(z.void(), async () =>
|
||||
useTransaction(async (tx) =>
|
||||
tx
|
||||
.select()
|
||||
.from(machineTable)
|
||||
.where(isNull(machineTable.timeDeleted))
|
||||
.then((rows) => rows.map(serialize))
|
||||
)
|
||||
)
|
||||
|
||||
export const fromID = fn(Info.shape.id, async (id) =>
|
||||
useTransaction(async (tx) =>
|
||||
tx
|
||||
.select()
|
||||
.from(machineTable)
|
||||
.where(and(eq(machineTable.id, id), isNull(machineTable.timeDeleted)))
|
||||
.then((rows) => rows.map(serialize).at(0))
|
||||
)
|
||||
)
|
||||
|
||||
export const fromFingerprint = fn(Info.shape.fingerprint, async (fingerprint) =>
|
||||
useTransaction(async (tx) =>
|
||||
tx
|
||||
.select()
|
||||
.from(machineTable)
|
||||
.where(and(eq(machineTable.fingerprint, fingerprint), isNull(machineTable.timeDeleted)))
|
||||
.execute()
|
||||
.then((rows) => rows.map(serialize).at(0))
|
||||
)
|
||||
)
|
||||
|
||||
export const remove = fn(Info.shape.id, (id) =>
|
||||
useTransaction(async (tx) => {
|
||||
await tx
|
||||
.update(machineTable)
|
||||
.set({
|
||||
timeDeleted: sql`now()`,
|
||||
})
|
||||
.where(and(eq(machineTable.id, id)))
|
||||
.execute();
|
||||
return id;
|
||||
}),
|
||||
);
|
||||
|
||||
export const fromLocation = fn(Info.shape.location, async (location) =>
|
||||
useTransaction(async (tx) => {
|
||||
const sqlDistance = sql`location <-> point(${location.longitude}, ${location.latitude})`;
|
||||
return tx
|
||||
.select({
|
||||
...getTableColumns(machineTable),
|
||||
distance: sql`round((${sqlDistance})::numeric, 2)`
|
||||
})
|
||||
.from(machineTable)
|
||||
.where(isNull(machineTable.timeDeleted)) //Should have a status update
|
||||
.orderBy(sqlDistance)
|
||||
.limit(3)
|
||||
.then((rows) => rows.map(serialize))
|
||||
})
|
||||
)
|
||||
|
||||
export function serialize(
|
||||
input: typeof machineTable.$inferSelect,
|
||||
): z.infer<typeof Info> {
|
||||
return {
|
||||
id: input.id,
|
||||
country: input.country,
|
||||
timezone: input.timezone,
|
||||
fingerprint: input.fingerprint,
|
||||
countryCode: input.countryCode,
|
||||
location: { latitude: input.location.y, longitude: input.location.x },
|
||||
};
|
||||
}
|
||||
}
|
||||
37
packages/core/src/machine/machine.sql.ts
Normal file
37
packages/core/src/machine/machine.sql.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { } from "drizzle-orm/postgres-js";
|
||||
import { timestamps, id } from "../drizzle/types";
|
||||
import {
|
||||
text,
|
||||
varchar,
|
||||
pgTable,
|
||||
uniqueIndex,
|
||||
point,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
export const machineTable = pgTable(
|
||||
"machine",
|
||||
{
|
||||
...id,
|
||||
...timestamps,
|
||||
country: text('country').notNull(),
|
||||
timezone: text('timezone').notNull(),
|
||||
location: point('location', { mode: 'xy' }).notNull(),
|
||||
fingerprint: varchar('fingerprint', { length: 32 }).notNull(),
|
||||
countryCode: varchar('country_code', { length: 2 }).notNull(),
|
||||
// provider: text("provider").notNull(),
|
||||
// gpuType: text("gpu_type").notNull(),
|
||||
// storage: numeric("storage").notNull(),
|
||||
// ipaddress: text("ipaddress").notNull(),
|
||||
// gpuNumber: integer("gpu_number").notNull(),
|
||||
// computePrice: numeric("compute_price").notNull(),
|
||||
// driverVersion: integer("driver_version").notNull(),
|
||||
// operatingSystem: text("operating_system").notNull(),
|
||||
// fingerprint: varchar("fingerprint", { length: 32 }).notNull(),
|
||||
// externalID: varchar("external_id", { length: 255 }).notNull(),
|
||||
// cudaVersion: numeric("cuda_version", { precision: 4, scale: 2 }).notNull(),
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex("external_id").on(table.externalID),
|
||||
uniqueIndex("machine_fingerprint").on(table.fingerprint)
|
||||
],
|
||||
);
|
||||
@@ -78,16 +78,16 @@ export module Member {
|
||||
}),
|
||||
);
|
||||
|
||||
export const remove = fn(Info.shape.id, (input) =>
|
||||
export const remove = fn(Info.shape.id, (id) =>
|
||||
useTransaction(async (tx) => {
|
||||
await tx
|
||||
.update(memberTable)
|
||||
.set({
|
||||
timeDeleted: sql`now()`,
|
||||
})
|
||||
.where(and(eq(memberTable.id, input), eq(memberTable.teamID, useTeam())))
|
||||
.where(and(eq(memberTable.id, id), eq(memberTable.teamID, useTeam())))
|
||||
.execute();
|
||||
return input;
|
||||
return id;
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -98,11 +98,10 @@ export module Member {
|
||||
.from(memberTable)
|
||||
.where(and(eq(memberTable.email, email), isNull(memberTable.timeDeleted)))
|
||||
.orderBy(asc(memberTable.timeCreated))
|
||||
.then((rows) => rows.map(serialize))
|
||||
.then((rows) => rows.at(0))
|
||||
),
|
||||
.then((rows) => rows.map(serialize).at(0))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
export const fromID = fn(z.string(), async (id) =>
|
||||
useTransaction(async (tx) =>
|
||||
tx
|
||||
@@ -110,8 +109,7 @@ export module Member {
|
||||
.from(memberTable)
|
||||
.where(and(eq(memberTable.id, id), isNull(memberTable.timeDeleted)))
|
||||
.orderBy(asc(memberTable.timeCreated))
|
||||
.then((rows) => rows.map(serialize))
|
||||
.then((rows) => rows.at(0))
|
||||
.then((rows) => rows.map(serialize).at(0))
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
24
packages/core/src/realtime/index.ts
Normal file
24
packages/core/src/realtime/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
IoTDataPlaneClient,
|
||||
PublishCommand,
|
||||
} from "@aws-sdk/client-iot-data-plane";
|
||||
import {useMachine} from "../actor";
|
||||
import {Resource} from "sst";
|
||||
|
||||
export module Realtime {
|
||||
const client = new IoTDataPlaneClient({});
|
||||
|
||||
export async function publish(message: any, subTopic?: string) {
|
||||
const fingerprint = useMachine();
|
||||
let topic = `${Resource.App.name}/${Resource.App.stage}/${fingerprint}/`;
|
||||
if (subTopic)
|
||||
topic = `${topic}${subTopic}`;
|
||||
|
||||
await client.send(
|
||||
new PublishCommand({
|
||||
payload: Buffer.from(JSON.stringify(message)),
|
||||
topic: topic,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
20
packages/core/src/task/task.sql.todo
Normal file
20
packages/core/src/task/task.sql.todo
Normal file
@@ -0,0 +1,20 @@
|
||||
import { id, timestamps } from "../drizzle/types";
|
||||
import { pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core";
|
||||
|
||||
//This represents a task created on a machine for running a game
|
||||
//Add billing info here?
|
||||
//Add who owns the task here
|
||||
// Add the session ID here
|
||||
//Add which machine owns this task
|
||||
|
||||
export const taskTable = pgTable(
|
||||
"task",
|
||||
{
|
||||
...id,
|
||||
...timestamps,
|
||||
fingerprint: varchar('fingerprint', { length: 32 }).notNull(),
|
||||
},
|
||||
(table) => [
|
||||
uniqueIndex("task_fingerprint").on(table.fingerprint),
|
||||
],
|
||||
);
|
||||
@@ -5,7 +5,7 @@ import { Common } from "../common";
|
||||
import { Examples } from "../examples";
|
||||
import { createEvent } from "../event";
|
||||
import { createID, fn } from "../utils";
|
||||
import { and, eq, sql } from "../drizzle";
|
||||
import { and, eq, sql, isNull } from "../drizzle";
|
||||
import { PlanType, teamTable } from "./team.sql";
|
||||
import { assertActor, withActor } from "../actor";
|
||||
import { memberTable } from "../member/member.sql";
|
||||
@@ -111,6 +111,7 @@ export module Team {
|
||||
tx
|
||||
.select()
|
||||
.from(teamTable)
|
||||
.where(isNull(teamTable.timeDeleted))
|
||||
.execute()
|
||||
.then((rows) => rows.map(serialize)),
|
||||
),
|
||||
@@ -121,10 +122,9 @@ export module Team {
|
||||
return tx
|
||||
.select()
|
||||
.from(teamTable)
|
||||
.where(eq(teamTable.id, id))
|
||||
.where(and(eq(teamTable.id, id), isNull(teamTable.timeDeleted)))
|
||||
.execute()
|
||||
.then((rows) => rows.map(serialize))
|
||||
.then((rows) => rows.at(0));
|
||||
.then((rows) => rows.map(serialize).at(0))
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -133,10 +133,9 @@ export module Team {
|
||||
return tx
|
||||
.select()
|
||||
.from(teamTable)
|
||||
.where(eq(teamTable.slug, input))
|
||||
.where(and(eq(teamTable.slug, input), isNull(teamTable.timeDeleted)))
|
||||
.execute()
|
||||
.then((rows) => rows.map(serialize))
|
||||
.then((rows) => rows.at(0));
|
||||
.then((rows) => rows.map(serialize).at(0))
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -183,16 +183,16 @@ export module User {
|
||||
};
|
||||
}
|
||||
|
||||
export const remove = fn(Info.shape.id, (input) =>
|
||||
export const remove = fn(Info.shape.id, (id) =>
|
||||
useTransaction(async (tx) => {
|
||||
await tx
|
||||
.update(userTable)
|
||||
.set({
|
||||
timeDeleted: sql`now()`,
|
||||
})
|
||||
.where(and(eq(userTable.id, input)))
|
||||
.where(and(eq(userTable.id, id)))
|
||||
.execute();
|
||||
return input;
|
||||
return id;
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ import { ulid } from "ulid";
|
||||
export const prefixes = {
|
||||
user: "usr",
|
||||
team: "tea",
|
||||
member: "mbr"
|
||||
task: "tsk",
|
||||
machine: "mch",
|
||||
member: "mbr",
|
||||
} as const;
|
||||
|
||||
export function createID(prefix: keyof typeof prefixes): string {
|
||||
|
||||
Reference in New Issue
Block a user