Format
This commit is contained in:
@@ -664,10 +664,7 @@ export async function handleTokenRevoke(
|
||||
|
||||
if (row.token_type === "refresh") {
|
||||
// Revoke refresh token and all its access tokens
|
||||
await db
|
||||
.deleteFrom("_emdash_oauth_tokens")
|
||||
.where("refresh_token_hash", "=", hash)
|
||||
.execute();
|
||||
await db.deleteFrom("_emdash_oauth_tokens").where("refresh_token_hash", "=", hash).execute();
|
||||
await db.deleteFrom("_emdash_oauth_tokens").where("token_hash", "=", hash).execute();
|
||||
} else {
|
||||
// Revoke just the access token
|
||||
|
||||
@@ -236,11 +236,7 @@ export async function handleOAuthClientUpdate(
|
||||
updates.scopes = input.scopes ? JSON.stringify(input.scopes) : "";
|
||||
}
|
||||
|
||||
await db
|
||||
.updateTable("_emdash_oauth_clients")
|
||||
.set(updates)
|
||||
.where("id", "=", clientId)
|
||||
.execute();
|
||||
await db.updateTable("_emdash_oauth_clients").set(updates).where("id", "=", clientId).execute();
|
||||
|
||||
// Fetch the updated row
|
||||
const updated = await db
|
||||
|
||||
@@ -95,11 +95,7 @@ export const PUT: APIRoute = async ({ params, request, locals }) => {
|
||||
if (newStatus === "approved" && previousStatus !== "approved" && emdash.email) {
|
||||
try {
|
||||
const adminBaseUrl = await getSiteBaseUrl(emdash.db, request);
|
||||
const content = await lookupContentAuthor(
|
||||
emdash.db,
|
||||
updated.collection,
|
||||
updated.contentId,
|
||||
);
|
||||
const content = await lookupContentAuthor(emdash.db, updated.collection, updated.contentId);
|
||||
if (content?.author) {
|
||||
await sendCommentNotification({
|
||||
email: emdash.email,
|
||||
|
||||
@@ -71,9 +71,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
||||
// Get passkey name - prefer body.name, then check stored pending name
|
||||
let passKeyName: string | undefined = body.name ?? undefined;
|
||||
if (!passKeyName) {
|
||||
const pending = await optionsRepo.get<{ name?: string }>(
|
||||
`emdash:passkey_pending:${user.id}`,
|
||||
);
|
||||
const pending = await optionsRepo.get<{ name?: string }>(`emdash:passkey_pending:${user.id}`);
|
||||
if (pending?.name) {
|
||||
passKeyName = pending.name;
|
||||
}
|
||||
|
||||
@@ -59,11 +59,7 @@ export const POST: APIRoute = async ({ params, request, locals, cache }) => {
|
||||
const denied = requireOwnerPerm(user, authorId, "content:publish_own", "content:publish_any");
|
||||
if (denied) return denied;
|
||||
|
||||
const result = await emdash.handleContentSchedule(
|
||||
collection,
|
||||
resolvedId ?? id,
|
||||
body.scheduledAt,
|
||||
);
|
||||
const result = await emdash.handleContentSchedule(collection, resolvedId ?? id, body.scheduledAt);
|
||||
|
||||
if (!result.success) return unwrapResult(result);
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
*/
|
||||
|
||||
import type { APIRoute } from "astro";
|
||||
import mime from "mime/lite";
|
||||
import { parseWxrString, SchemaRegistry, type WxrData } from "emdash";
|
||||
import mime from "mime/lite";
|
||||
|
||||
import { requirePerm } from "#api/authorize.js";
|
||||
import { apiError, apiSuccess, handleError } from "#api/error.js";
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
import * as path from "node:path";
|
||||
|
||||
import type { APIRoute } from "astro";
|
||||
import mime from "mime/lite";
|
||||
import { MediaRepository, computeContentHash } from "emdash";
|
||||
import mime from "mime/lite";
|
||||
import { ulid } from "ulidx";
|
||||
|
||||
import { requirePerm } from "#api/authorize.js";
|
||||
|
||||
@@ -15,11 +15,7 @@ export const POST: APIRoute = async ({ params, locals }) => {
|
||||
const { emdash, user } = locals;
|
||||
const revisionId = params.revisionId!;
|
||||
|
||||
if (
|
||||
!emdash?.handleRevisionRestore ||
|
||||
!emdash?.handleRevisionGet ||
|
||||
!emdash?.handleContentGet
|
||||
) {
|
||||
if (!emdash?.handleRevisionRestore || !emdash?.handleRevisionGet || !emdash?.handleContentGet) {
|
||||
return apiError("NOT_CONFIGURED", "EmDash not configured", 500);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,11 +57,7 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
|
||||
// Update sort_order for each widget
|
||||
await Promise.all(
|
||||
body.widgetIds.map((id, index) =>
|
||||
db
|
||||
.updateTable("_emdash_widgets")
|
||||
.set({ sort_order: index })
|
||||
.where("id", "=", id)
|
||||
.execute(),
|
||||
db.updateTable("_emdash_widgets").set({ sort_order: index }).where("id", "=", id).execute(),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -418,9 +418,7 @@ export const publishCommand = defineCommand({
|
||||
process.exit(1);
|
||||
}
|
||||
} catch {
|
||||
consola.error(
|
||||
"No dist/ directory found. Run `emdash plugin bundle` first or use --build.",
|
||||
);
|
||||
consola.error("No dist/ directory found. Run `emdash plugin bundle` first or use --build.");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,9 +135,7 @@ export const seedCommand = defineCommand({
|
||||
const seedPath = await resolveSeedPath(cwd, args.path);
|
||||
if (!seedPath) {
|
||||
consola.error("No seed file found");
|
||||
consola.info(
|
||||
"Provide a path, create .emdash/seed.json, or set emdash.seed in package.json",
|
||||
);
|
||||
consola.info("Provide a path, create .emdash/seed.json, or set emdash.seed in package.json");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
|
||||
.execute();
|
||||
|
||||
// ── Device code polling tracking ─────────────────────────────────
|
||||
await db.schema
|
||||
.alterTable("_emdash_device_codes")
|
||||
.addColumn("last_polled_at", "text")
|
||||
.execute();
|
||||
await db.schema.alterTable("_emdash_device_codes").addColumn("last_polled_at", "text").execute();
|
||||
}
|
||||
|
||||
export async function down(db: Kysely<unknown>): Promise<void> {
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
* The handlers instance is passed per-request via authInfo on the transport.
|
||||
*/
|
||||
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
||||
import type { Permission, RoleLevel } from "@emdash-cms/auth";
|
||||
import { canActOnOwn, Role } from "@emdash-cms/auth";
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
||||
import { z } from "zod";
|
||||
|
||||
import type { EmDashHandlers } from "../astro/types.js";
|
||||
|
||||
@@ -103,11 +103,7 @@ export class LocalStorage implements Storage {
|
||||
size: buffer.length,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new EmDashStorageError(
|
||||
`Failed to upload file: ${options.key}`,
|
||||
"UPLOAD_FAILED",
|
||||
error,
|
||||
);
|
||||
throw new EmDashStorageError(`Failed to upload file: ${options.key}`, "UPLOAD_FAILED", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,11 +97,7 @@ export class S3Storage implements Storage {
|
||||
size: body.length,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new EmDashStorageError(
|
||||
`Failed to upload file: ${options.key}`,
|
||||
"UPLOAD_FAILED",
|
||||
error,
|
||||
);
|
||||
throw new EmDashStorageError(`Failed to upload file: ${options.key}`, "UPLOAD_FAILED", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,11 +162,7 @@ export class S3Storage implements Storage {
|
||||
if (hasErrorName(error) && error.name === "NotFound") {
|
||||
return false;
|
||||
}
|
||||
throw new EmDashStorageError(
|
||||
`Failed to check file existence: ${key}`,
|
||||
"HEAD_FAILED",
|
||||
error,
|
||||
);
|
||||
throw new EmDashStorageError(`Failed to check file existence: ${key}`, "HEAD_FAILED", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -172,11 +172,7 @@ describe("Device Flow: Full Lifecycle", () => {
|
||||
});
|
||||
|
||||
it("should handle denied authorization", async () => {
|
||||
const codeResult = await handleDeviceCodeRequest(
|
||||
db,
|
||||
{},
|
||||
"https://example.com/_emdash/device",
|
||||
);
|
||||
const codeResult = await handleDeviceCodeRequest(db, {}, "https://example.com/_emdash/device");
|
||||
expect(codeResult.success).toBe(true);
|
||||
if (!codeResult.success) return;
|
||||
|
||||
@@ -199,11 +195,7 @@ describe("Device Flow: Full Lifecycle", () => {
|
||||
});
|
||||
|
||||
it("should normalize user codes (strip hyphens, case-insensitive)", async () => {
|
||||
const codeResult = await handleDeviceCodeRequest(
|
||||
db,
|
||||
{},
|
||||
"https://example.com/_emdash/device",
|
||||
);
|
||||
const codeResult = await handleDeviceCodeRequest(db, {}, "https://example.com/_emdash/device");
|
||||
expect(codeResult.success).toBe(true);
|
||||
if (!codeResult.success) return;
|
||||
|
||||
@@ -263,11 +255,7 @@ describe("Device Token Exchange: Error Cases", () => {
|
||||
describe("Token Refresh", () => {
|
||||
it("should exchange a refresh token for a new access token", async () => {
|
||||
// Complete a device flow first to get tokens
|
||||
const codeResult = await handleDeviceCodeRequest(
|
||||
db,
|
||||
{},
|
||||
"https://example.com/_emdash/device",
|
||||
);
|
||||
const codeResult = await handleDeviceCodeRequest(db, {}, "https://example.com/_emdash/device");
|
||||
expect(codeResult.success).toBe(true);
|
||||
if (!codeResult.success) return;
|
||||
|
||||
@@ -330,11 +318,7 @@ describe("Token Refresh", () => {
|
||||
describe("Token Revoke", () => {
|
||||
it("should revoke an access token", async () => {
|
||||
// Get tokens via device flow
|
||||
const codeResult = await handleDeviceCodeRequest(
|
||||
db,
|
||||
{},
|
||||
"https://example.com/_emdash/device",
|
||||
);
|
||||
const codeResult = await handleDeviceCodeRequest(db, {}, "https://example.com/_emdash/device");
|
||||
if (!codeResult.success) return;
|
||||
|
||||
await handleDeviceAuthorize(db, "user-1", Role.ADMIN, {
|
||||
@@ -365,11 +349,7 @@ describe("Token Revoke", () => {
|
||||
|
||||
it("should revoke a refresh token and its access tokens", async () => {
|
||||
// Get tokens via device flow
|
||||
const codeResult = await handleDeviceCodeRequest(
|
||||
db,
|
||||
{},
|
||||
"https://example.com/_emdash/device",
|
||||
);
|
||||
const codeResult = await handleDeviceCodeRequest(db, {}, "https://example.com/_emdash/device");
|
||||
if (!codeResult.success) return;
|
||||
|
||||
await handleDeviceAuthorize(db, "user-1", Role.ADMIN, {
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
* authInfo to simulate different users and roles.
|
||||
*/
|
||||
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
||||
import { Role } from "@emdash-cms/auth";
|
||||
import type { RoleLevel } from "@emdash-cms/auth";
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { EmDashHandlers } from "../../../src/astro/types.js";
|
||||
|
||||
@@ -77,10 +77,7 @@ describe("normalizeMediaValue", () => {
|
||||
});
|
||||
|
||||
it("falls back to external for internal URL when local provider unavailable", async () => {
|
||||
const result = await normalizeMediaValue(
|
||||
"/_emdash/api/media/file/01ABC.jpg",
|
||||
getProvider({}),
|
||||
);
|
||||
const result = await normalizeMediaValue("/_emdash/api/media/file/01ABC.jpg", getProvider({}));
|
||||
expect(result).toEqual({
|
||||
provider: "external",
|
||||
id: "",
|
||||
|
||||
Reference in New Issue
Block a user