mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 16:55:37 +02:00
⭐feat: Add more API endpoints (#150)
This commit is contained in:
@@ -49,6 +49,7 @@ export function useCurrentUser() {
|
||||
id:actor.properties.userID,
|
||||
token: actor.properties.accessToken
|
||||
};
|
||||
|
||||
throw new VisibleError(
|
||||
"auth",
|
||||
"unauthorized",
|
||||
|
||||
@@ -3,10 +3,10 @@ import { init } from "@instantdb/admin";
|
||||
import schema from "../instant.schema";
|
||||
|
||||
const databaseClient = () => init({
|
||||
appId: Resource.InstantAppId.value,
|
||||
adminToken: Resource.InstantAdminToken.value,
|
||||
schema
|
||||
})
|
||||
appId: Resource.InstantAppId.value,
|
||||
adminToken: Resource.InstantAdminToken.value,
|
||||
schema
|
||||
})
|
||||
|
||||
|
||||
export default databaseClient
|
||||
@@ -7,39 +7,23 @@ export module Examples {
|
||||
|
||||
export const Machine = {
|
||||
id: "0bfcb712-df13-4454-81a8-fbee66eddca4",
|
||||
hostname: "desktopeuo8vsf",
|
||||
hostname: "DESKTOP-EUO8VSF",
|
||||
fingerprint: "fc27f428f9ca47d4b41b70889ae0c62090",
|
||||
location: "KE, AF"
|
||||
createdAt: '2025-01-04T11:56:23.902Z',
|
||||
deletedAt: '2025-01-09T01:56:23.902Z'
|
||||
}
|
||||
|
||||
// export const Team = {
|
||||
// id: createID(),
|
||||
// name: "Jane's Family",
|
||||
// type: "Family"
|
||||
// }
|
||||
|
||||
// export const ProductVariant = {
|
||||
// id: createID(),
|
||||
// name: "FamilySM",
|
||||
// price: 10,
|
||||
// };
|
||||
|
||||
// export const Product = {
|
||||
// id: createID(),
|
||||
// name: "Family",
|
||||
// description: "The ideal subscription tier for dedicated gamers who crave more flexibility and social gaming experiences.",
|
||||
// variants: [ProductVariant],
|
||||
// subscription: "allowed" as const,
|
||||
// };
|
||||
|
||||
// export const Subscription = {
|
||||
// id: createID(),
|
||||
// productVariant: ProductVariant,
|
||||
// quantity: 1,
|
||||
// polarOrderID: createID(),
|
||||
// frequency: "monthly" as const,
|
||||
// next: new Date("2024-02-01 19:36:19.000").getTime(),
|
||||
// owner: User
|
||||
// };
|
||||
export const Game = {
|
||||
id: '0bfcb712-df13-4454-81a8-fbee66eddca4',
|
||||
name: "Control Ultimate Edition",
|
||||
steamID: 870780,
|
||||
}
|
||||
|
||||
export const Session = {
|
||||
id: "0bfcb712-df13-4454-81a8-fbee66eddca4",
|
||||
public: true,
|
||||
name: 'Late night chilling with the squad',
|
||||
startedAt: '2025-01-04T11:56:23.902Z',
|
||||
endedAt: '2025-01-04T11:56:23.902Z'
|
||||
}
|
||||
}
|
||||
151
packages/core/src/game/index.ts
Normal file
151
packages/core/src/game/index.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import { z } from "zod"
|
||||
import { fn } from "../utils";
|
||||
import { Common } from "../common";
|
||||
import { Examples } from "../examples";
|
||||
import databaseClient from "../database"
|
||||
import { id as createID } from "@instantdb/admin";
|
||||
import { groupBy, map, pipe, values } from "remeda"
|
||||
import { useCurrentDevice, useCurrentUser } from "../actor";
|
||||
|
||||
export module Games {
|
||||
export const Info = z
|
||||
.object({
|
||||
id: z.string().openapi({
|
||||
description: Common.IdDescription,
|
||||
example: Examples.Game.id,
|
||||
}),
|
||||
name: z.string().openapi({
|
||||
description: "A human-readable name for the game, used for easy identification.",
|
||||
example: Examples.Game.name,
|
||||
}),
|
||||
steamID: z.number().openapi({
|
||||
description: "The Steam ID of the game, used to identify it during installation and runtime.",
|
||||
example: Examples.Game.steamID,
|
||||
})
|
||||
})
|
||||
.openapi({
|
||||
ref: "Game",
|
||||
description: "Represents a Steam game that can be installed and played on a machine.",
|
||||
example: Examples.Game,
|
||||
});
|
||||
|
||||
export type Info = z.infer<typeof Info>;
|
||||
|
||||
export const create = fn(Info.pick({ name: true, steamID: true }), async (input) => {
|
||||
const id = createID()
|
||||
const db = databaseClient()
|
||||
const device = useCurrentDevice()
|
||||
|
||||
await db.transact(
|
||||
db.tx.games[id]!.update({
|
||||
name: input.name,
|
||||
steamID: input.steamID,
|
||||
}).link({ machines: device.id })
|
||||
)
|
||||
|
||||
return id
|
||||
})
|
||||
|
||||
export const list = async () => {
|
||||
const db = databaseClient()
|
||||
const user = useCurrentUser()
|
||||
|
||||
const query = {
|
||||
$users: {
|
||||
$: { where: { id: user.id } },
|
||||
games: {}
|
||||
},
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
|
||||
const games = res.$users[0]?.games
|
||||
if (games && games.length > 0) {
|
||||
const result = pipe(
|
||||
games,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
name: group[0].name,
|
||||
steamID: group[0].steamID,
|
||||
}))
|
||||
)
|
||||
return result
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const fromSteamID = fn(z.number(), async (steamID) => {
|
||||
const db = databaseClient()
|
||||
|
||||
const query = {
|
||||
games: {
|
||||
$: {
|
||||
where: {
|
||||
steamID,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
|
||||
const games = res.games
|
||||
|
||||
if (games.length > 0) {
|
||||
const result = pipe(
|
||||
games,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
name: group[0].name,
|
||||
steamID: group[0].steamID,
|
||||
}))
|
||||
)
|
||||
return result[0]
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
export const linkToCurrentUser = fn(z.string(), async (steamID) => {
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient()
|
||||
|
||||
await db.transact(db.tx.games[steamID]!.link({ owners: user.id }))
|
||||
|
||||
return "ok"
|
||||
})
|
||||
|
||||
export const unLinkFromCurrentUser = fn(z.number(), async (steamID) => {
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient()
|
||||
|
||||
const query = {
|
||||
$users: {
|
||||
$: { where: { id: user.id } },
|
||||
games: {
|
||||
$: {
|
||||
where: {
|
||||
steamID,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
const games = res.$users[0]?.games
|
||||
if (games && games.length > 0) {
|
||||
const game = games[0] as Info
|
||||
await db.transact(db.tx.games[game.id]!.unlink({ owners: user.id }))
|
||||
|
||||
return "ok"
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
}
|
||||
@@ -2,11 +2,12 @@ import { z } from "zod"
|
||||
import { fn } from "../utils";
|
||||
import { Common } from "../common";
|
||||
import { Examples } from "../examples";
|
||||
import databaseClient from "../database"
|
||||
import { useCurrentUser } from "../actor";
|
||||
import databaseClient from "../database"
|
||||
import { id as createID } from "@instantdb/admin";
|
||||
|
||||
export module Machine {
|
||||
import { groupBy, map, pipe, values } from "remeda"
|
||||
import { Games } from "../game"
|
||||
export module Machines {
|
||||
export const Info = z
|
||||
.object({
|
||||
id: z.string().openapi({
|
||||
@@ -14,63 +15,45 @@ export module Machine {
|
||||
example: Examples.Machine.id,
|
||||
}),
|
||||
hostname: z.string().openapi({
|
||||
description: "Hostname of the machine",
|
||||
description: "The Linux hostname that identifies this machine",
|
||||
example: Examples.Machine.hostname,
|
||||
}),
|
||||
fingerprint: z.string().openapi({
|
||||
description: "The machine's fingerprint, derived from the machine's Linux machine ID.",
|
||||
description: "A unique identifier derived from the machine's Linux machine ID.",
|
||||
example: Examples.Machine.fingerprint,
|
||||
}),
|
||||
location: z.string().openapi({
|
||||
description: "The machine's approximate location; country and continent.",
|
||||
example: Examples.Machine.location,
|
||||
createdAt: z.string().or(z.number()).openapi({
|
||||
description: "Represents a machine running on the Nestri network, containing its identifying information and metadata.",
|
||||
example: Examples.Machine.createdAt,
|
||||
})
|
||||
})
|
||||
.openapi({
|
||||
ref: "Machine",
|
||||
description: "A machine running on the Nestri network.",
|
||||
description: "Represents a a physical or virtual machine connected to the Nestri network..",
|
||||
example: Examples.Machine,
|
||||
});
|
||||
|
||||
export const create = fn(z.object({
|
||||
fingerprint: z.string(),
|
||||
hostname: z.string(),
|
||||
location: z.string()
|
||||
}), async (input) => {
|
||||
export type Info = z.infer<typeof Info>;
|
||||
|
||||
export const create = fn(Info.pick({ fingerprint: true, hostname: true }), async (input) => {
|
||||
const id = createID()
|
||||
const now = new Date().getTime()
|
||||
const now = new Date().toISOString()
|
||||
const db = databaseClient()
|
||||
await db.transact(
|
||||
db.tx.machines[id]!.update({
|
||||
fingerprint: input.fingerprint,
|
||||
hostname: input.hostname,
|
||||
location: input.location,
|
||||
createdAt: now,
|
||||
//Just in case it had been previously deleted
|
||||
deletedAt: undefined
|
||||
})
|
||||
)
|
||||
|
||||
return id
|
||||
})
|
||||
|
||||
export const remove = fn(z.string(), async (id) => {
|
||||
const now = new Date().getTime()
|
||||
// const device = useCurrentDevice()
|
||||
// const db = databaseClient()
|
||||
|
||||
// if (device.id) { // the machine can delete itself
|
||||
// await db.transact(db.tx.machines[device.id]!.update({ deletedAt: now }))
|
||||
// } else {// the user can delete it manually
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient().asUser({ token: user.token })
|
||||
await db.transact(db.tx.machines[id]!.update({ deletedAt: now }))
|
||||
// }
|
||||
|
||||
return "ok"
|
||||
})
|
||||
|
||||
export const fromID = fn(z.string(), async (id) => {
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient().asUser({ token: user.token })
|
||||
const db = databaseClient()
|
||||
|
||||
const query = {
|
||||
machines: {
|
||||
@@ -84,8 +67,53 @@ export module Machine {
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
const machines = res.machines
|
||||
|
||||
return res.machines[0]
|
||||
if (machines && machines.length > 0) {
|
||||
const result = pipe(
|
||||
machines,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
fingerprint: group[0].fingerprint,
|
||||
hostname: group[0].hostname,
|
||||
createdAt: group[0].createdAt
|
||||
}))
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
export const installedGames = fn(z.string(), async (id) => {
|
||||
const db = databaseClient()
|
||||
|
||||
const query = {
|
||||
machines: {
|
||||
$: {
|
||||
where: {
|
||||
id: id,
|
||||
deletedAt: { $isNull: true }
|
||||
}
|
||||
},
|
||||
games: {}
|
||||
}
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
const machines = res.machines
|
||||
|
||||
if (machines && machines.length > 0) {
|
||||
const games = machines[0]?.games as any
|
||||
if (games.length > 0) {
|
||||
return games as Games.Info[]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
export const fromFingerprint = fn(z.string(), async (input) => {
|
||||
@@ -104,19 +132,38 @@ export module Machine {
|
||||
|
||||
const res = await db.query(query)
|
||||
|
||||
return res.machines[0]
|
||||
const machines = res.machines
|
||||
|
||||
if (machines.length > 0) {
|
||||
const result = pipe(
|
||||
machines,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
fingerprint: group[0].fingerprint,
|
||||
hostname: group[0].hostname,
|
||||
createdAt: group[0].createdAt
|
||||
}))
|
||||
)
|
||||
return result[0]
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
export const list = async () => {
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient().asUser({ token: user.token })
|
||||
const db = databaseClient()
|
||||
|
||||
const query = {
|
||||
$users: {
|
||||
$: { where: { id: user.id } },
|
||||
machines: {
|
||||
$: {
|
||||
deletedAt: { $isNull: true }
|
||||
where: {
|
||||
deletedAt: { $isNull: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -124,17 +171,62 @@ export module Machine {
|
||||
|
||||
const res = await db.query(query)
|
||||
|
||||
return res.$users[0]?.machines
|
||||
const machines = res.$users[0]?.machines
|
||||
if (machines && machines.length > 0) {
|
||||
const result = pipe(
|
||||
machines,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
fingerprint: group[0].fingerprint,
|
||||
hostname: group[0].hostname,
|
||||
createdAt: group[0].createdAt
|
||||
}))
|
||||
)
|
||||
return result
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const link = fn(z.object({
|
||||
machineId: z.string()
|
||||
}), async (input) => {
|
||||
export const linkToCurrentUser = fn(z.string(), async (id) => {
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient()
|
||||
|
||||
await db.transact(db.tx.machines[input.machineId]!.link({ owner: user.id }))
|
||||
await db.transact(db.tx.machines[id]!.link({ owner: user.id }))
|
||||
|
||||
return "ok"
|
||||
})
|
||||
|
||||
export const unLinkFromCurrentUser = fn(z.string(), async (id) => {
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient()
|
||||
const now = new Date().toISOString()
|
||||
|
||||
const query = {
|
||||
$users: {
|
||||
$: { where: { id: user.id } },
|
||||
machines: {
|
||||
$: {
|
||||
where: {
|
||||
id,
|
||||
deletedAt: { $isNull: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
const machines = res.$users[0]?.machines
|
||||
if (machines && machines.length > 0) {
|
||||
const machine = machines[0] as Info
|
||||
await db.transact(db.tx.machines[machine.id]!.update({ deletedAt: now }))
|
||||
|
||||
return "ok"
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
}
|
||||
292
packages/core/src/session/index.ts
Normal file
292
packages/core/src/session/index.ts
Normal file
@@ -0,0 +1,292 @@
|
||||
import { z } from "zod"
|
||||
import { fn } from "../utils";
|
||||
import { Machines } from "../machine";
|
||||
import { Common } from "../common";
|
||||
import { Examples } from "../examples";
|
||||
import databaseClient from "../database"
|
||||
import { useCurrentUser } from "../actor";
|
||||
import { groupBy, map, pipe, values } from "remeda"
|
||||
import { id as createID } from "@instantdb/admin";
|
||||
|
||||
export module Sessions {
|
||||
export const Info = z
|
||||
.object({
|
||||
id: z.string().openapi({
|
||||
description: Common.IdDescription,
|
||||
example: Examples.Session.id,
|
||||
}),
|
||||
name: z.string().openapi({
|
||||
description: "A human-readable name for the session to help identify it",
|
||||
example: Examples.Session.name,
|
||||
}),
|
||||
public: z.boolean().openapi({
|
||||
description: "If true, the session is publicly viewable by all users. If false, only authorized users can access it",
|
||||
example: Examples.Session.public,
|
||||
}),
|
||||
endedAt: z.string().or(z.number()).or(z.undefined()).openapi({
|
||||
description: "The timestamp indicating when this session was completed or terminated. Null if session is still active.",
|
||||
example: Examples.Session.endedAt,
|
||||
}),
|
||||
startedAt: z.string().or(z.number()).openapi({
|
||||
description: "The timestamp indicating when this session started.",
|
||||
example: Examples.Session.startedAt,
|
||||
})
|
||||
})
|
||||
.openapi({
|
||||
ref: "Session",
|
||||
description: "Represents a single game play session, tracking its lifetime and accessibility settings.",
|
||||
example: Examples.Session,
|
||||
});
|
||||
|
||||
export type Info = z.infer<typeof Info>;
|
||||
|
||||
export const create = fn(z.object({ name: z.string(), public: z.boolean(), fingerprint: z.string(), steamID: z.number() }), async (input) => {
|
||||
const id = createID()
|
||||
const now = new Date().toISOString()
|
||||
const db = databaseClient()
|
||||
const user = useCurrentUser()
|
||||
const machine = await Machines.fromFingerprint(input.fingerprint)
|
||||
if (!machine) {
|
||||
return { error: "Such a machine does not exist" }
|
||||
}
|
||||
|
||||
const games = await Machines.installedGames(machine.id)
|
||||
|
||||
if (!games) {
|
||||
return { error: "The machine has no installed games" }
|
||||
}
|
||||
|
||||
const result = pipe(
|
||||
games,
|
||||
groupBy(x => x.steamID === input.steamID ? "similar" : undefined),
|
||||
)
|
||||
|
||||
if (!result.similar || result.similar.length == 0) {
|
||||
|
||||
return { error: "The machine does not have this game installed" }
|
||||
}
|
||||
|
||||
await db.transact(
|
||||
db.tx.sessions[id]!.update({
|
||||
name: input.name,
|
||||
public: input.public,
|
||||
startedAt: now,
|
||||
}).link({ owner: user.id, machine: machine.id, game: result.similar[0].id })
|
||||
)
|
||||
|
||||
return { data: id }
|
||||
})
|
||||
|
||||
export const list = async () => {
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient()
|
||||
|
||||
const query = {
|
||||
$users: {
|
||||
$: { where: { id: user.id } },
|
||||
sessions: {}
|
||||
},
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
|
||||
const sessions = res.$users[0]?.sessions
|
||||
if (sessions && sessions.length > 0) {
|
||||
const result = pipe(
|
||||
sessions,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
endedAt: group[0].endedAt,
|
||||
startedAt: group[0].startedAt,
|
||||
public: group[0].public,
|
||||
name: group[0].name
|
||||
}))
|
||||
)
|
||||
return result
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const getActive = async () => {
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient()
|
||||
|
||||
const query = {
|
||||
$users: {
|
||||
$: { where: { id: user.id } },
|
||||
sessions: {
|
||||
$: {
|
||||
where: {
|
||||
endedAt: { $isNull: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
|
||||
const sessions = res.$users[0]?.sessions
|
||||
if (sessions && sessions.length > 0) {
|
||||
const result = pipe(
|
||||
sessions,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
endedAt: group[0].endedAt,
|
||||
startedAt: group[0].startedAt,
|
||||
public: group[0].public,
|
||||
name: group[0].name
|
||||
}))
|
||||
)
|
||||
return result
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const getPublicActive = async () => {
|
||||
const db = databaseClient()
|
||||
|
||||
const query = {
|
||||
sessions: {
|
||||
$: {
|
||||
where: {
|
||||
endedAt: { $isNull: true },
|
||||
public: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
|
||||
const sessions = res.sessions
|
||||
if (sessions && sessions.length > 0) {
|
||||
const result = pipe(
|
||||
sessions,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
endedAt: group[0].endedAt,
|
||||
startedAt: group[0].startedAt,
|
||||
public: group[0].public,
|
||||
name: group[0].name
|
||||
}))
|
||||
)
|
||||
return result
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const fromSteamID = fn(z.number(), async (steamID) => {
|
||||
const db = databaseClient()
|
||||
|
||||
const query = {
|
||||
games: {
|
||||
$: {
|
||||
where: {
|
||||
steamID
|
||||
}
|
||||
},
|
||||
sessions: {
|
||||
$: {
|
||||
where: {
|
||||
endedAt: { $isNull: true },
|
||||
public: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
|
||||
const sessions = res.games[0]?.sessions
|
||||
if (sessions && sessions.length > 0) {
|
||||
const result = pipe(
|
||||
sessions,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
endedAt: group[0].endedAt,
|
||||
startedAt: group[0].startedAt,
|
||||
public: group[0].public,
|
||||
name: group[0].name
|
||||
}))
|
||||
)
|
||||
return result
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
export const fromID = fn(z.string(), async (id) => {
|
||||
const db = databaseClient()
|
||||
useCurrentUser()
|
||||
|
||||
const query = {
|
||||
sessions: {
|
||||
$: {
|
||||
where: {
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
const sessions = res.sessions
|
||||
|
||||
if (sessions && sessions.length > 0) {
|
||||
const result = pipe(
|
||||
sessions,
|
||||
groupBy(x => x.id),
|
||||
values(),
|
||||
map((group): Info => ({
|
||||
id: group[0].id,
|
||||
endedAt: group[0].endedAt,
|
||||
startedAt: group[0].startedAt,
|
||||
public: group[0].public,
|
||||
name: group[0].name
|
||||
}))
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
export const end = fn(z.string(), async (id) => {
|
||||
const user = useCurrentUser()
|
||||
const db = databaseClient()
|
||||
const now = new Date().toISOString()
|
||||
|
||||
const query = {
|
||||
$users: {
|
||||
$: { where: { id: user.id } },
|
||||
sessions: {
|
||||
$: {
|
||||
where: {
|
||||
id,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const res = await db.query(query)
|
||||
const sessions = res.$users[0]?.sessions
|
||||
if (sessions && sessions.length > 0) {
|
||||
const session = sessions[0] as Info
|
||||
await db.transact(db.tx.sessions[session.id]!.update({ endedAt: now }))
|
||||
|
||||
return "ok"
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import databaseClient from "../database"
|
||||
import { z } from "zod"
|
||||
import { Common } from "../common";
|
||||
import { createID, fn } from "../utils";
|
||||
import { Examples } from "../examples";
|
||||
|
||||
export module Team {
|
||||
export const Info = z
|
||||
.object({
|
||||
id: z.string().openapi({
|
||||
description: Common.IdDescription,
|
||||
example: Examples.Team.id,
|
||||
}),
|
||||
name: z.string().openapi({
|
||||
description: "Name of the machine",
|
||||
example: Examples.Team.name,
|
||||
}),
|
||||
type: z.string().nullable().openapi({
|
||||
description: "Whether this is a personal or family type of team",
|
||||
example: Examples.Team.type,
|
||||
})
|
||||
})
|
||||
.openapi({
|
||||
ref: "Team",
|
||||
description: "A group of Nestri user's who share the same machine",
|
||||
example: Examples.Team,
|
||||
});
|
||||
|
||||
export const create = fn(z.object({
|
||||
name: z.string(),
|
||||
type: z.enum(["personal", "family"]),
|
||||
owner: z.string(),
|
||||
}), async (input) => {
|
||||
const id = createID("machine")
|
||||
const now = new Date().getTime()
|
||||
const db = databaseClient()
|
||||
|
||||
await db.transact(db.tx.teams[id]!.update({
|
||||
name: input.name,
|
||||
type: input.type,
|
||||
createdAt: now
|
||||
}).link({
|
||||
owner: input.owner,
|
||||
}))
|
||||
|
||||
return id
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user