Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions functions/api/qrcode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Cloudflare Pages Function - QR Code API Proxy
* 解決 CORS 問題並代理請求到 QRCode Monkey API
* https://www.qrcode-monkey.com/qr-code-api-with-logo
*/
export async function onRequestPost(context) {
try {
const request = context.request;
const payload = await request.json();

console.log("Received request");

// 如果是獲取已生成的圖片
if (payload.imageUrl) {
const imageUrl = "https://api.qrcode-monkey.com" + payload.imageUrl;
console.log("Fetching image from:", imageUrl);

const imageResponse = await fetch(imageUrl);

if (!imageResponse.ok) {
return new Response(
JSON.stringify({ error: "Failed to fetch image" }),
{
status: imageResponse.status,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
},
);
}

const imageBlob = await imageResponse.blob();
return new Response(imageBlob, {
headers: {
"Content-Type":
imageResponse.headers.get("content-type") || "image/png",
"Access-Control-Allow-Origin": "*",
"Cache-Control": "public, max-age=3600",
},
});
}

// 原本的 QR Code 生成邏輯
console.log("Generating QR Code");

const response = await fetch("https://api.qrcode-monkey.com/qr/custom", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});

if (!response.ok) {
const errorText = await response.text();
console.error("QRCode Monkey API Error:", errorText);
return new Response(
JSON.stringify({
error: "API request failed",
status: response.status,
details: errorText,
}),
{
status: response.status,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
},
},
);
}

const contentType = response.headers.get("content-type");
console.log("Response content type:", contentType);

if (contentType && contentType.includes("image")) {
const imageBlob = await response.blob();
return new Response(imageBlob, {
headers: {
"Content-Type": contentType,
"Access-Control-Allow-Origin": "*",
"Cache-Control": "public, max-age=3600",
},
});
}

const data = await response.json();
return new Response(JSON.stringify(data), {
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
},
});
} catch (error) {
console.error("Function Error:", error);
return new Response(
JSON.stringify({
error: "Internal server error",
message: error.message,
}),
{
status: 500,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
},
);
}
}

export async function onRequestOptions() {
return new Response(null, {
status: 204,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Max-Age": "86400",
},
});
}
62 changes: 13 additions & 49 deletions src/pages/qrcode_generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,10 @@ const QRCodeGeneratorPage = () => {

console.log("Sending payload:", JSON.stringify(payload, null, 2));

// 使用 CORS 代理(僅限開發環境)
const isDevelopment = process.env.NODE_ENV === "development";
const apiUrl = isDevelopment
? "https://corsproxy.io/?https://api.qrcode-monkey.com/qr/custom"
: "https://api.qrcode-monkey.com/qr/custom";

try {
// 透過 functions/api/qrcode
const apiUrl = "/api/qrcode";

const response = await fetch(apiUrl, {
method: "POST",
headers: {
Expand Down Expand Up @@ -82,12 +79,17 @@ const QRCodeGeneratorPage = () => {
const imageUrl = "https://api.qrcode-monkey.com" + result.imageUrl;
console.log("Fetching image from:", imageUrl);

// 使用 CORS 代理(僅限開發環境)
const imgUrl = isDevelopment
? `https://corsproxy.io/?${imageUrl}`
: imageUrl;
// 透過 functions/api/qrcode
const imgResponse = await fetch("/api/qrcode", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
imageUrl: result.imageUrl,
}),
});

const imgResponse = await fetch(imgUrl);
if (imgResponse.ok) {
const blob = await imgResponse.blob();
const url = URL.createObjectURL(blob);
Expand Down Expand Up @@ -320,44 +322,6 @@ const QRCodeGeneratorPage = () => {
</div>
</div>
)}

{/* 使用說明 */}
<div className="relative bg-white rounded-xl p-6 md:p-8 mb-8 border border-gray-300 shadow-lg">
<span
className="absolute left-0 top-0 bottom-0 w-1 bg-btnbg rounded-l-xl"
aria-hidden="true"
></span>
<h4 className="text-lg font-bold text-gray-800 mb-4 flex items-center gap-2">
<Icon icon="mdi:lightbulb-on" className="text-btnbg" />
使用說明
</h4>
<div className="space-y-3 text-gray-800">
<div className="flex items-start gap-2">
<span className="bg-btnbg text-white text-sm px-2 py-1 rounded font-mono">
1
</span>
<span>在輸入框中輸入網址內容</span>
</div>
<div className="flex items-start gap-2">
<span className="bg-btnbg text-white text-sm px-2 py-1 rounded font-mono">
2
</span>
<span>點擊「生成 QR Code」按鈕或按 Enter 鍵</span>
</div>
<div className="flex items-start gap-2">
<span className="bg-btnbg text-white text-sm px-2 py-1 rounded font-mono">
3
</span>
<span>等待 QR Code 生成並預覽</span>
</div>
<div className="flex items-start gap-2">
<span className="bg-btnbg text-white text-sm px-2 py-1 rounded font-mono">
4
</span>
<span>點擊「下載 QR Code 圖片」按鈕保存圖片檔案</span>
</div>
</div>
</div>
</div>
</div>
<Footer />
Expand Down
52 changes: 50 additions & 2 deletions src/pages/swift_res.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,59 @@ const SwiftPage = () => {
</div>
{/* 空白區 */}
<div className="h-16" />
{/* 使用 Swift 開發教學指南 */}
<div className="mx-2 md:mx-12 flex flex-col lg:flex-row py-8 px-4 md:px-16 border border-gray-400 rounded-3xl shadow-2xl gap-7 items-center">
<div className="flex flex-col gap-4 xl:w-1/2">
<h2 className="text-3xl font-bold break-normal">
使用 Swift 開發教學指南<br></br>(Develop in Swift Tutorials)
</h2>
<p className="py-10 break-normal">
此教學指南為學習開發 Apple 平臺應用程式的初學者,介紹使用 Swift
語言與 Xcode 進行應用程式開發的基礎知識。
</p>
<div className="flex items-center gap-2">
<button
className="overflow-hidden group h-12 px-6 border border-btnbg rounded-full"
onClick={() => {
window.open(
"https://developer.apple.com/tutorials/develop-in-swift",
"_blank",
);
}}
>
<div className="transition duration-200 group-hover:-translate-y-12">
<div className="h-12 flex items-center justify-center text-btnbg break-words ">
<a className="text-blue-800 text-xl">立刻前往</a>
</div>
<div className="h-12 flex items-center justify-center text-btnbg break-words ">
<a className="text-blue-800 text-xl">Go</a>
</div>
</div>
</button>
</div>
</div>
<div className="w-full lg:w-1/2 flex border border-gray-400 rounded-3xl shadow-2xl">
<div className="flex">
<div className="flex flex-col">
<ImageWithPlaceholder
src="https://docs-assets.developer.apple.com/published/b1016c987253d597d9e7790e135111f3/foundations--0hero.png"
alt="Develop in Swift Tutorials"
className="w-full"
imgClassName="object-contain"
aspectRatio="4/3"
objectFit="contain"
/>
</div>
</div>
</div>
</div>
{/* 空白區 */}
<div className="h-16" />
{/* Swift 書 */}
<div className="mx-2 md:mx-12 flex flex-col lg:flex-row py-8 px-4 md:px-16 border border-gray-400 rounded-3xl shadow-2xl gap-7 items-center">
<div className="flex flex-col gap-4">
<h2 className="text-3xl font-bold break-normal text-center">
在 Swift 課程中學開發
在 Swift 課程中學習開發手冊
</h2>
<p className="py-10 break-normal mx-0 lg:mx-24">
開始新的課程或是將您的技能提升到一個新水平。
Expand All @@ -82,7 +130,7 @@ const SwiftPage = () => {
alt={item.title}
className="w-full"
imgClassName="object-contain"
aspectRatio="3/4"
aspectRatio="16/9"
objectFit="contain"
/>
<p className="break-normal text-2xl font-black text-left py-4">
Expand Down