Skip to content

Commit

Permalink
ok-4-1
Browse files Browse the repository at this point in the history
  • Loading branch information
aigem committed Sep 12, 2024
1 parent aab18f1 commit e161610
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 49 deletions.
5 changes: 3 additions & 2 deletions src/handlers/requestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ export async function handleRequest(request: Request, env: Env, ctx: ExecutionCo
});
}

const response = await handleWebDAV(request, env.BUCKET, env.BUCKET_NAME);
// 直接传递整个 env 对象给 handleWebDAV
const response = await handleWebDAV(request, env);

setCORSHeaders(response, request);
return response;
} catch (error) {
logger.error("Error in request handling:", error);
return new Response("Internal Server Error", { status: 500 });
}
}
}
81 changes: 49 additions & 32 deletions src/handlers/webdavHandler.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
// 文件名:src/handlers/webdavHandler.ts
import { listAll, fromR2Object, make_resource_path, generatePropfindResponse } from '../utils/webdavUtils';
import { logger } from '../utils/logger';
import { generateHTML, generateErrorHTML } from '../utils/templates';
import { WebDAVProps } from '../types';
import { WebDAVProps, Env } from '../types';
import { authenticate } from '../utils/auth';

const SUPPORT_METHODS = ["OPTIONS", "PROPFIND", "MKCOL", "GET", "HEAD", "PUT", "COPY", "MOVE", "DELETE"];
const DAV_CLASS = "1, 2";

export async function handleWebDAV(request: Request, bucket: R2Bucket, bucketName: string): Promise<Response> {
export async function handleWebDAV(request: Request, env: Env): Promise<Response> {
const { BUCKET, BUCKET_NAME } = env; // 从 env 中获取 BUCKET 和 BUCKET_NAME

try {
switch (request.method) {
// 原来的处理逻辑不变
case "OPTIONS":
return handleOptions();
case "HEAD":
return await handleHead(request, bucket);
return await handleHead(request, BUCKET);
case "GET":
return await handleGet(request, bucket, bucketName);
return await handleGet(request, BUCKET, BUCKET_NAME);
case "PUT":
return await handlePut(request, bucket);
return await handlePut(request, BUCKET);
case "DELETE":
return await handleDelete(request, bucket);
return await handleDelete(request, BUCKET);
case "MKCOL":
return await handleMkcol(request, bucket);
return await handleMkcol(request, BUCKET);
case "PROPFIND":
return await handlePropfind(request, bucket, bucketName);
return await handlePropfind(request, BUCKET, BUCKET_NAME);
case "COPY":
return await handleCopy(request, bucket);
return await handleCopy(request, BUCKET);
case "MOVE":
return await handleMove(request, bucket);
return await handleMove(request, BUCKET);
default:
return new Response("Method Not Allowed", {
status: 405,
Expand All @@ -36,8 +41,9 @@ export async function handleWebDAV(request: Request, bucket: R2Bucket, bucketNam
}
});
}
} catch (error) { const err = error as Error;
logger.error("Error in WebDAV handling:", error);
} catch (error) {
const err = error as Error;
logger.error("Error in WebDAV handling:", err.message);
return new Response(generateErrorHTML("Internal Server Error", err.message), {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" }
Expand All @@ -50,7 +56,13 @@ function handleOptions(): Response {
status: 200,
headers: {
Allow: SUPPORT_METHODS.join(", "),
DAV: DAV_CLASS
DAV: DAV_CLASS,
"Access-Control-Allow-Methods": SUPPORT_METHODS.join(", "),
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Authorization, Content-Type, Depth, Overwrite, Destination, Range",
"Access-Control-Expose-Headers": "Content-Type, Content-Length, DAV, ETag, Last-Modified, Location, Date, Content-Range",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Max-Age": "86400"
}
});
}
Expand Down Expand Up @@ -78,10 +90,8 @@ async function handleGet(request: Request, bucket: R2Bucket, bucketName: string)
const resource_path = make_resource_path(request);

if (request.url.endsWith("/")) {
// 处理目录
return await handleDirectory(bucket, resource_path, bucketName);
} else {
// 处理文件
return await handleFile(bucket, resource_path);
}
}
Expand All @@ -101,8 +111,9 @@ async function handleDirectory(bucket: R2Bucket, resource_path: string, bucketNa
const href = `/${object.key}${isDirectory ? "/" : ""}`;
items.push({ name: `${isDirectory ? '📁 ' : '📄 '}${displayName}`, href });
}
} catch (error) { const err = error as Error;
logger.error("Error listing objects:", error);
} catch (error) {
const err = error as Error;
logger.error("Error listing objects:", err.message);
return new Response(generateErrorHTML("Error listing directory contents", err.message), {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" }
Expand Down Expand Up @@ -131,8 +142,9 @@ async function handleFile(bucket: R2Bucket, resource_path: string): Promise<Resp
"Last-Modified": object.uploaded.toUTCString()
}
});
} catch (error) { const err = error as Error;
logger.error("Error getting object:", error);
} catch (error) {
const err = error as Error;
logger.error("Error getting object:", err.message);
return new Response(generateErrorHTML("Error retrieving file", err.message), {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" }
Expand All @@ -151,8 +163,9 @@ async function handlePut(request: Request, bucket: R2Bucket): Promise<Response>
},
});
return new Response("Created", { status: 201 });
} catch (error) { const err = error as Error;
logger.error("Error uploading file:", error);
} catch (error) {
const err = error as Error;
logger.error("Error uploading file:", err.message);
return new Response(generateErrorHTML("Error uploading file", err.message), {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" }
Expand All @@ -166,8 +179,9 @@ async function handleDelete(request: Request, bucket: R2Bucket): Promise<Respons
try {
await bucket.delete(resource_path);
return new Response("No Content", { status: 204 });
} catch (error) { const err = error as Error;
logger.error("Error deleting object:", error);
} catch (error) {
const err = error as Error;
logger.error("Error deleting object:", err.message);
return new Response(generateErrorHTML("Error deleting file", err.message), {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" }
Expand All @@ -183,13 +197,13 @@ async function handleMkcol(request: Request, bucket: R2Bucket): Promise<Response
}

try {
// 尝试通过上传空数据来创建目录
await bucket.put(resource_path + "/", new Uint8Array(), {
customMetadata: { resourcetype: "collection" }
});
return new Response("Created", { status: 201 });
} catch (error) { const err = error as Error;
logger.error("Error creating collection:", error);
} catch (error) {
const err = error as Error;
logger.error("Error creating collection:", err.message);
return new Response(generateErrorHTML("Error creating collection", err.message), {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" }
Expand Down Expand Up @@ -222,8 +236,9 @@ async function handlePropfind(request: Request, bucket: R2Bucket, bucketName: st
status: 207,
headers: { "Content-Type": "application/xml; charset=utf-8" }
});
} catch (error) { const err = error as Error;
logger.error("Error in PROPFIND:", error);
} catch (error) {
const err = error as Error;
logger.error("Error in PROPFIND:", err.message);
return new Response(generateErrorHTML("Error in PROPFIND", err.message), {
status: 500,
headers: { "Content-Type": "application/xml; charset=utf-8" }
Expand Down Expand Up @@ -252,8 +267,9 @@ async function handleCopy(request: Request, bucket: R2Bucket): Promise<Response>
});

return new Response("Created", { status: 201 });
} catch (error) { const err = error as Error;
logger.error("Error copying object:", error);
} catch (error) {
const err = error as Error;
logger.error("Error copying object:", err.message);
return new Response(generateErrorHTML("Error copying file", err.message), {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" }
Expand Down Expand Up @@ -283,8 +299,9 @@ async function handleMove(request: Request, bucket: R2Bucket): Promise<Response>

await bucket.delete(sourcePath);
return new Response("No Content", { status: 204 });
} catch (error) { const err = error as Error;
logger.error("Error moving object:", error);
} catch (error) {
const err = error as Error;
logger.error("Error moving object:", err.message);
return new Response(generateErrorHTML("Error moving file", err.message), {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" }
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// 文件名:src/index.ts
import { handleRequest } from './handlers/requestHandler';
import { Env } from './types';

Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// 文件名:src/types.ts
export interface Env {
BUCKET: R2Bucket;
USERNAME: string;
PASSWORD: string;
BUCKET_NAME: string; // 新增的环境变量
BUCKET_NAME: string;
}

export interface CacheableResponse {
Expand Down
9 changes: 3 additions & 6 deletions src/utils/auth.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// 文件名:src/utils/auth.ts
import { Env } from '../types';

export function authenticate(request: Request, env: Env): boolean {
Expand All @@ -11,10 +12,6 @@ export function authenticate(request: Request, env: Env): boolean {
return false;
}

try {
const [username, password] = atob(authValue).split(':');
return username === env.USERNAME && password === env.PASSWORD;
} catch (e) {
return false;
}
const [username, password] = atob(authValue).split(':');
return username === env.USERNAME && password === env.PASSWORD;
}
5 changes: 3 additions & 2 deletions src/utils/cors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// 文件名:src/utils/cors.ts
export function setCORSHeaders(response: Response, request: Request): void {
const origin = request.headers.get("Origin");
if (origin) {
Expand All @@ -8,5 +9,5 @@ export function setCORSHeaders(response: Response, request: Request): void {
response.headers.set("Access-Control-Allow-Headers", "Authorization, Content-Type, Depth, Overwrite, Destination, Range");
response.headers.set("Access-Control-Expose-Headers", "Content-Type, Content-Length, DAV, ETag, Last-Modified, Location, Date, Content-Range");
response.headers.set("Access-Control-Allow-Credentials", "true");
response.headers.set("Access-Control-Max-Age", "86400"); // Cache preflight for 24 hours
}
response.headers.set("Access-Control-Max-Age", "86400");
}
1 change: 1 addition & 0 deletions src/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// 文件名:src/utils/logger.ts
export const logger = {
info: (message: string, ...args: any[]) => console.log(`[INFO] ${new Date().toISOString()} - ${message}`, ...args),
error: (message: string, ...args: any[]) => console.error(`[ERROR] ${new Date().toISOString()} - ${message}`, ...args),
Expand Down
9 changes: 3 additions & 6 deletions src/utils/webdavUtils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
// 文件名:src/utils/webdavUtils.ts
import { R2Object } from '@cloudflare/workers-types';
import { WebDAVProps } from '../types';

export function make_resource_path(request: Request): string {
const url = new URL(request.url);
let path = decodeURIComponent(url.pathname.slice(1)); // 移除初始的 '/'

// 处理 Windows 特殊字符
path = path.replace(/\\/g, '/');
return path;
return decodeURIComponent(url.pathname.slice(1));
}

export async function* listAll(bucket: R2Bucket, prefix: string) {
Expand Down Expand Up @@ -76,4 +73,4 @@ function generatePropResponse(bucketName: string, basePath: string, prop: WebDAV
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>`;
}
}

0 comments on commit e161610

Please sign in to comment.