Compare commits
2 Commits
2f059ceaff
...
2665fa1c02
Author | SHA1 | Date |
---|---|---|
pinks | 2665fa1c02 | |
pinks | 083f6bc01c |
|
@ -0,0 +1,9 @@
|
||||||
|
import { createEndpoint, createMethodFilter } from "t_rest/server";
|
||||||
|
import { bot } from "../bot/mod.ts";
|
||||||
|
|
||||||
|
export const botRoute = createMethodFilter({
|
||||||
|
GET: createEndpoint({ query: null, body: null }, async () => {
|
||||||
|
const username = bot.botInfo.username;
|
||||||
|
return { status: 200, body: { type: "application/json", data: { username } } };
|
||||||
|
}),
|
||||||
|
});
|
|
@ -2,8 +2,7 @@ import { deepMerge } from "std/collections/deep_merge.ts";
|
||||||
import { info } from "std/log/mod.ts";
|
import { info } from "std/log/mod.ts";
|
||||||
import { createEndpoint, createMethodFilter } from "t_rest/server";
|
import { createEndpoint, createMethodFilter } from "t_rest/server";
|
||||||
import { configSchema, getConfig, setConfig } from "../app/config.ts";
|
import { configSchema, getConfig, setConfig } from "../app/config.ts";
|
||||||
import { bot } from "../bot/mod.ts";
|
import { withUser } from "./withUser.ts";
|
||||||
import { sessions } from "./sessionsRoute.ts";
|
|
||||||
|
|
||||||
export const paramsRoute = createMethodFilter({
|
export const paramsRoute = createMethodFilter({
|
||||||
GET: createEndpoint(
|
GET: createEndpoint(
|
||||||
|
@ -23,23 +22,13 @@ export const paramsRoute = createMethodFilter({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async ({ query, body }) => {
|
async ({ query, body }) => {
|
||||||
const session = sessions.get(query.sessionId);
|
return withUser(query, async (chat) => {
|
||||||
if (!session?.userId) {
|
const config = await getConfig();
|
||||||
return { status: 401, body: { type: "text/plain", data: "Must be logged in" } };
|
info(`User ${chat.username} updated default params: ${JSON.stringify(body.data)}`);
|
||||||
}
|
const defaultParams = deepMerge(config.defaultParams ?? {}, body.data);
|
||||||
const chat = await bot.api.getChat(session.userId);
|
await setConfig({ defaultParams });
|
||||||
if (chat.type !== "private") throw new Error("Chat is not private");
|
return { status: 200, body: { type: "application/json", data: config.defaultParams } };
|
||||||
if (!chat.username) {
|
}, { admin: true });
|
||||||
return { status: 403, body: { type: "text/plain", data: "Must have a username" } };
|
|
||||||
}
|
|
||||||
const config = await getConfig();
|
|
||||||
if (!config?.adminUsernames?.includes(chat.username)) {
|
|
||||||
return { status: 403, body: { type: "text/plain", data: "Must be an admin" } };
|
|
||||||
}
|
|
||||||
info(`User ${chat.username} updated default params: ${JSON.stringify(body.data)}`);
|
|
||||||
const defaultParams = deepMerge(config.defaultParams ?? {}, body.data);
|
|
||||||
await setConfig({ defaultParams });
|
|
||||||
return { status: 200, body: { type: "application/json", data: config.defaultParams } };
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
import {
|
import { createLoggerMiddleware, createPathFilter } from "t_rest/server";
|
||||||
createEndpoint,
|
import { botRoute } from "./botRoute.ts";
|
||||||
createLoggerMiddleware,
|
|
||||||
createMethodFilter,
|
|
||||||
createPathFilter,
|
|
||||||
} from "t_rest/server";
|
|
||||||
import { jobsRoute } from "./jobsRoute.ts";
|
import { jobsRoute } from "./jobsRoute.ts";
|
||||||
import { paramsRoute } from "./paramsRoute.ts";
|
import { paramsRoute } from "./paramsRoute.ts";
|
||||||
import { sessionsRoute } from "./sessionsRoute.ts";
|
import { sessionsRoute } from "./sessionsRoute.ts";
|
||||||
import { statsRoute } from "./statsRoute.ts";
|
import { statsRoute } from "./statsRoute.ts";
|
||||||
import { usersRoute } from "./usersRoute.ts";
|
import { usersRoute } from "./usersRoute.ts";
|
||||||
import { workersRoute } from "./workersRoute.ts";
|
import { workersRoute } from "./workersRoute.ts";
|
||||||
import { bot } from "../bot/mod.ts";
|
|
||||||
|
|
||||||
export const serveApi = createLoggerMiddleware(
|
export const serveApi = createLoggerMiddleware(
|
||||||
createPathFilter({
|
createPathFilter({
|
||||||
|
@ -20,13 +15,7 @@ export const serveApi = createLoggerMiddleware(
|
||||||
"settings/params": paramsRoute,
|
"settings/params": paramsRoute,
|
||||||
"stats": statsRoute,
|
"stats": statsRoute,
|
||||||
"workers": workersRoute,
|
"workers": workersRoute,
|
||||||
"bot": createMethodFilter({
|
"bot": botRoute,
|
||||||
// deno-lint-ignore require-await
|
|
||||||
GET: createEndpoint({ query: null, body: null }, async () => {
|
|
||||||
const username = bot.botInfo.username;
|
|
||||||
return { status: 200, body: { type: "application/json", data: { username } } };
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
{ filterStatus: (status) => status >= 400 },
|
{ filterStatus: (status) => status >= 400 },
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// deno-lint-ignore-file require-await
|
|
||||||
import { createEndpoint, createMethodFilter, createPathFilter } from "t_rest/server";
|
import { createEndpoint, createMethodFilter, createPathFilter } from "t_rest/server";
|
||||||
import { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Chat } from "grammy_types";
|
||||||
|
import { Output } from "t_rest/client";
|
||||||
|
import { getConfig } from "../app/config.ts";
|
||||||
|
import { bot } from "../bot/mod.ts";
|
||||||
|
import { sessions } from "./sessionsRoute.ts";
|
||||||
|
|
||||||
|
export async function withUser<O extends Output>(
|
||||||
|
query: { sessionId: string },
|
||||||
|
cb: (user: Chat.PrivateGetChat) => Promise<O>,
|
||||||
|
options?: { admin?: boolean },
|
||||||
|
) {
|
||||||
|
const session = sessions.get(query.sessionId);
|
||||||
|
if (!session?.userId) {
|
||||||
|
return { status: 401, body: { type: "text/plain", data: "Must be logged in" } } as const;
|
||||||
|
}
|
||||||
|
const chat = await bot.api.getChat(session.userId);
|
||||||
|
if (chat.type !== "private") throw new Error("Chat is not private");
|
||||||
|
if (options?.admin) {
|
||||||
|
if (!chat.username) {
|
||||||
|
return { status: 403, body: { type: "text/plain", data: "Must have a username" } } as const;
|
||||||
|
}
|
||||||
|
const config = await getConfig();
|
||||||
|
if (!config?.adminUsernames?.includes(chat.username)) {
|
||||||
|
return { status: 403, body: { type: "text/plain", data: "Must be an admin" } } as const;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cb(chat);
|
||||||
|
}
|
|
@ -3,14 +3,16 @@ import { Model } from "indexed_kv";
|
||||||
import createOpenApiFetch from "openapi_fetch";
|
import createOpenApiFetch from "openapi_fetch";
|
||||||
import { info } from "std/log/mod.ts";
|
import { info } from "std/log/mod.ts";
|
||||||
import { createEndpoint, createMethodFilter, createPathFilter } from "t_rest/server";
|
import { createEndpoint, createMethodFilter, createPathFilter } from "t_rest/server";
|
||||||
import { getConfig } from "../app/config.ts";
|
|
||||||
import { activeGenerationWorkers } from "../app/generationQueue.ts";
|
import { activeGenerationWorkers } from "../app/generationQueue.ts";
|
||||||
import { generationStore } from "../app/generationStore.ts";
|
import { generationStore } from "../app/generationStore.ts";
|
||||||
import * as SdApi from "../app/sdApi.ts";
|
import * as SdApi from "../app/sdApi.ts";
|
||||||
import { WorkerInstance, workerInstanceStore } from "../app/workerInstanceStore.ts";
|
import {
|
||||||
import { bot } from "../bot/mod.ts";
|
WorkerInstance,
|
||||||
|
workerInstanceSchema,
|
||||||
|
workerInstanceStore,
|
||||||
|
} from "../app/workerInstanceStore.ts";
|
||||||
import { getAuthHeader } from "../utils/getAuthHeader.ts";
|
import { getAuthHeader } from "../utils/getAuthHeader.ts";
|
||||||
import { sessions } from "./sessionsRoute.ts";
|
import { withUser } from "./withUser.ts";
|
||||||
|
|
||||||
export type WorkerData = Omit<WorkerInstance, "sdUrl" | "sdAuth"> & {
|
export type WorkerData = Omit<WorkerInstance, "sdUrl" | "sdAuth"> & {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -69,7 +71,6 @@ export const workersRoute = createPathFilter({
|
||||||
async () => {
|
async () => {
|
||||||
const workerInstances = await workerInstanceStore.getAll();
|
const workerInstances = await workerInstanceStore.getAll();
|
||||||
const workers = await Promise.all(workerInstances.map(getWorkerData));
|
const workers = await Promise.all(workerInstances.map(getWorkerData));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: { type: "application/json", data: workers satisfies WorkerData[] },
|
body: { type: "application/json", data: workers satisfies WorkerData[] },
|
||||||
|
@ -83,51 +84,18 @@ export const workersRoute = createPathFilter({
|
||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
type: "application/json",
|
type: "application/json",
|
||||||
schema: {
|
schema: workerInstanceSchema,
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
key: { type: "string" },
|
|
||||||
name: { type: ["string", "null"] },
|
|
||||||
sdUrl: { type: "string" },
|
|
||||||
sdAuth: {
|
|
||||||
type: ["object", "null"],
|
|
||||||
properties: {
|
|
||||||
user: { type: "string" },
|
|
||||||
password: { type: "string" },
|
|
||||||
},
|
|
||||||
required: ["user", "password"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["key", "name", "sdUrl", "sdAuth"],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async ({ query, body }) => {
|
async ({ query, body }) => {
|
||||||
const session = sessions.get(query.sessionId);
|
return withUser(query, async (chat) => {
|
||||||
if (!session?.userId) {
|
const workerInstance = await workerInstanceStore.create(body.data);
|
||||||
return { status: 401, body: { type: "text/plain", data: "Must be logged in" } };
|
info(`User ${chat.username} created worker ${workerInstance.id}`);
|
||||||
}
|
return {
|
||||||
const chat = await bot.api.getChat(session.userId);
|
status: 200,
|
||||||
if (chat.type !== "private") throw new Error("Chat is not private");
|
body: { type: "application/json", data: await getWorkerData(workerInstance) },
|
||||||
if (!chat.username) {
|
};
|
||||||
return { status: 403, body: { type: "text/plain", data: "Must have a username" } };
|
}, { admin: true });
|
||||||
}
|
|
||||||
const config = await getConfig();
|
|
||||||
if (!config?.adminUsernames?.includes(chat.username)) {
|
|
||||||
return { status: 403, body: { type: "text/plain", data: "Must be an admin" } };
|
|
||||||
}
|
|
||||||
const workerInstance = await workerInstanceStore.create({
|
|
||||||
key: body.data.key,
|
|
||||||
name: body.data.name,
|
|
||||||
sdUrl: body.data.sdUrl,
|
|
||||||
sdAuth: body.data.sdAuth,
|
|
||||||
});
|
|
||||||
info(`User ${chat.username} created worker ${workerInstance.id}`);
|
|
||||||
const worker = await getWorkerData(workerInstance);
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: { type: "application/json", data: worker satisfies WorkerData },
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
@ -141,10 +109,9 @@ export const workersRoute = createPathFilter({
|
||||||
if (!workerInstance) {
|
if (!workerInstance) {
|
||||||
return { status: 404, body: { type: "text/plain", data: `Worker not found` } };
|
return { status: 404, body: { type: "text/plain", data: `Worker not found` } };
|
||||||
}
|
}
|
||||||
const worker: WorkerData = await getWorkerData(workerInstance);
|
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: { type: "application/json", data: worker satisfies WorkerData },
|
body: { type: "application/json", data: await getWorkerData(workerInstance) },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -155,22 +122,7 @@ export const workersRoute = createPathFilter({
|
||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
type: "application/json",
|
type: "application/json",
|
||||||
schema: {
|
schema: { ...workerInstanceSchema, required: [] },
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
key: { type: "string" },
|
|
||||||
name: { type: ["string", "null"] },
|
|
||||||
sdUrl: { type: "string" },
|
|
||||||
auth: {
|
|
||||||
type: ["object", "null"],
|
|
||||||
properties: {
|
|
||||||
user: { type: "string" },
|
|
||||||
password: { type: "string" },
|
|
||||||
},
|
|
||||||
required: ["user", "password"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async ({ params, query, body }) => {
|
async ({ params, query, body }) => {
|
||||||
|
@ -178,37 +130,18 @@ export const workersRoute = createPathFilter({
|
||||||
if (!workerInstance) {
|
if (!workerInstance) {
|
||||||
return { status: 404, body: { type: "text/plain", data: `Worker not found` } };
|
return { status: 404, body: { type: "text/plain", data: `Worker not found` } };
|
||||||
}
|
}
|
||||||
const session = sessions.get(query.sessionId);
|
return withUser(query, async (chat) => {
|
||||||
if (!session?.userId) {
|
info(
|
||||||
return { status: 401, body: { type: "text/plain", data: "Must be logged in" } };
|
`User ${chat.username} updated worker ${params.workerId}: ${
|
||||||
}
|
JSON.stringify(body.data)
|
||||||
const chat = await bot.api.getChat(session.userId);
|
}`,
|
||||||
if (chat.type !== "private") throw new Error("Chat is not private");
|
);
|
||||||
if (!chat.username) {
|
await workerInstance.update(body.data);
|
||||||
return { status: 403, body: { type: "text/plain", data: "Must have a username" } };
|
return {
|
||||||
}
|
status: 200,
|
||||||
const config = await getConfig();
|
body: { type: "application/json", data: await getWorkerData(workerInstance) },
|
||||||
if (!config?.adminUsernames?.includes(chat.username)) {
|
};
|
||||||
return { status: 403, body: { type: "text/plain", data: "Must be an admin" } };
|
}, { admin: true });
|
||||||
}
|
|
||||||
if (body.data.name !== undefined) {
|
|
||||||
workerInstance.value.name = body.data.name;
|
|
||||||
}
|
|
||||||
if (body.data.sdUrl !== undefined) {
|
|
||||||
workerInstance.value.sdUrl = body.data.sdUrl;
|
|
||||||
}
|
|
||||||
if (body.data.auth !== undefined) {
|
|
||||||
workerInstance.value.sdAuth = body.data.auth;
|
|
||||||
}
|
|
||||||
info(
|
|
||||||
`User ${chat.username} updated worker ${params.workerId}: ${JSON.stringify(body.data)}`,
|
|
||||||
);
|
|
||||||
await workerInstance.update();
|
|
||||||
const worker = await getWorkerData(workerInstance);
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: { type: "application/json", data: worker satisfies WorkerData },
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
DELETE: createEndpoint(
|
DELETE: createEndpoint(
|
||||||
|
@ -223,22 +156,11 @@ export const workersRoute = createPathFilter({
|
||||||
if (!workerInstance) {
|
if (!workerInstance) {
|
||||||
return { status: 404, body: { type: "text/plain", data: `Worker not found` } };
|
return { status: 404, body: { type: "text/plain", data: `Worker not found` } };
|
||||||
}
|
}
|
||||||
const session = sessions.get(query.sessionId);
|
return withUser(query, async (chat) => {
|
||||||
if (!session?.userId) {
|
info(`User ${chat.username} deleted worker ${params.workerId}`);
|
||||||
return { status: 401, body: { type: "text/plain", data: "Must be logged in" } };
|
await workerInstance.delete();
|
||||||
}
|
return { status: 200, body: { type: "application/json", data: null } };
|
||||||
const chat = await bot.api.getChat(session.userId);
|
}, { admin: true });
|
||||||
if (chat.type !== "private") throw new Error("Chat is not private");
|
|
||||||
if (!chat.username) {
|
|
||||||
return { status: 403, body: { type: "text/plain", data: "Must have a username" } };
|
|
||||||
}
|
|
||||||
const config = await getConfig();
|
|
||||||
if (!config?.adminUsernames?.includes(chat.username)) {
|
|
||||||
return { status: 403, body: { type: "text/plain", data: "Must be an admin" } };
|
|
||||||
}
|
|
||||||
info(`User ${chat.username} deleted worker ${params.workerId}`);
|
|
||||||
await workerInstance.delete();
|
|
||||||
return { status: 200, body: { type: "application/json", data: null } };
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -9,6 +9,13 @@
|
||||||
"fmt": {
|
"fmt": {
|
||||||
"lineWidth": 100
|
"lineWidth": 100
|
||||||
},
|
},
|
||||||
|
"lint": {
|
||||||
|
"rules": {
|
||||||
|
"exclude": [
|
||||||
|
"require-await"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"@date-fns/utc": "https://cdn.skypack.dev/@date-fns/utc@1.1.0?dts",
|
"@date-fns/utc": "https://cdn.skypack.dev/@date-fns/utc@1.1.0?dts",
|
||||||
"async": "https://deno.land/x/async@v2.0.2/mod.ts",
|
"async": "https://deno.land/x/async@v2.0.2/mod.ts",
|
||||||
|
|
|
@ -249,7 +249,7 @@ function WorkerListItem(props: { worker: WorkerData; sessionId?: string }) {
|
||||||
body: {
|
body: {
|
||||||
type: "application/json",
|
type: "application/json",
|
||||||
data: {
|
data: {
|
||||||
auth: user && password ? { user, password } : null,
|
sdAuth: user && password ? { user, password } : null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
User-agent: *
|
||||||
|
Disallow: /
|
Loading…
Reference in New Issue