Prettier format pending files
This commit is contained in:
parent
6cb7f3081d
commit
1a680a424f
17 changed files with 117 additions and 76 deletions
|
|
@ -36,7 +36,8 @@ interface GhostApiResponse {
|
||||||
|
|
||||||
async function fetchFromGhost(slug: string): Promise<any | null> {
|
async function fetchFromGhost(slug: string): Promise<any | null> {
|
||||||
const ghostUrl = process.env.GHOST_API_URL || process.env.VITE_GHOST_API_URL;
|
const ghostUrl = process.env.GHOST_API_URL || process.env.VITE_GHOST_API_URL;
|
||||||
const ghostKey = process.env.GHOST_CONTENT_API_KEY || process.env.VITE_GHOST_CONTENT_API_KEY;
|
const ghostKey =
|
||||||
|
process.env.GHOST_CONTENT_API_KEY || process.env.VITE_GHOST_CONTENT_API_KEY;
|
||||||
|
|
||||||
if (!ghostUrl || !ghostKey) {
|
if (!ghostUrl || !ghostKey) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ interface GhostApiResponse {
|
||||||
|
|
||||||
async function fetchFromGhost(limit: number = 50): Promise<any[]> {
|
async function fetchFromGhost(limit: number = 50): Promise<any[]> {
|
||||||
const ghostUrl = process.env.GHOST_API_URL || process.env.VITE_GHOST_API_URL;
|
const ghostUrl = process.env.GHOST_API_URL || process.env.VITE_GHOST_API_URL;
|
||||||
const ghostKey = process.env.GHOST_CONTENT_API_KEY || process.env.VITE_GHOST_CONTENT_API_KEY;
|
const ghostKey =
|
||||||
|
process.env.GHOST_CONTENT_API_KEY || process.env.VITE_GHOST_CONTENT_API_KEY;
|
||||||
|
|
||||||
if (!ghostUrl || !ghostKey) {
|
if (!ghostUrl || !ghostKey) {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import { createClient } from "@supabase/supabase-js";
|
import { createClient } from "@supabase/supabase-js";
|
||||||
import { publishPostToGhost, updatePostInGhost } from "../../server/ghost-admin-api";
|
import {
|
||||||
|
publishPostToGhost,
|
||||||
|
updatePostInGhost,
|
||||||
|
} from "../../server/ghost-admin-api";
|
||||||
|
|
||||||
const supabaseUrl = process.env.SUPABASE_URL || "";
|
const supabaseUrl = process.env.SUPABASE_URL || "";
|
||||||
const supabaseServiceRole = process.env.SUPABASE_SERVICE_ROLE || "";
|
const supabaseServiceRole = process.env.SUPABASE_SERVICE_ROLE || "";
|
||||||
|
|
@ -42,7 +45,9 @@ export default async function handler(req: any, res: any) {
|
||||||
// Check if user is admin or staff
|
// Check if user is admin or staff
|
||||||
const isAuthorized = await isUserAdminOrStaff(userId);
|
const isAuthorized = await isUserAdminOrStaff(userId);
|
||||||
if (!isAuthorized) {
|
if (!isAuthorized) {
|
||||||
return res.status(403).json({ error: "Forbidden: Admin/Staff access required" });
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "Forbidden: Admin/Staff access required" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,11 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
|
|
||||||
const overdue = (invoices || [])
|
const overdue = (invoices || [])
|
||||||
.filter((i: any) => i.status === "overdue")
|
.filter((i: any) => i.status === "overdue")
|
||||||
.reduce((sum: number, i: any) => sum + ((i.amount_due || 0) - (i.amount_paid || 0)), 0);
|
.reduce(
|
||||||
|
(sum: number, i: any) =>
|
||||||
|
sum + ((i.amount_due || 0) - (i.amount_paid || 0)),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
const activeContracts = (contracts || []).filter(
|
const activeContracts = (contracts || []).filter(
|
||||||
(c: any) => c.status === "active",
|
(c: any) => c.status === "active",
|
||||||
|
|
@ -120,12 +124,14 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
pending_invoices: (invoices || []).filter(
|
pending_invoices: (invoices || []).filter(
|
||||||
(i: any) => i.status === "pending" || i.status === "sent",
|
(i: any) => i.status === "pending" || i.status === "sent",
|
||||||
).length,
|
).length,
|
||||||
overdue_invoices: (invoices || []).filter((i: any) => i.status === "overdue")
|
overdue_invoices: (invoices || []).filter(
|
||||||
.length,
|
(i: any) => i.status === "overdue",
|
||||||
|
).length,
|
||||||
},
|
},
|
||||||
health: {
|
health: {
|
||||||
payment_rate: Math.round((totalPaid / totalInvoiced) * 100) || 0,
|
payment_rate: Math.round((totalPaid / totalInvoiced) * 100) || 0,
|
||||||
contract_completion_rate: completedContracts / (activeContracts + completedContracts) || 0,
|
contract_completion_rate:
|
||||||
|
completedContracts / (activeContracts + completedContracts) || 0,
|
||||||
cash_flow_status:
|
cash_flow_status:
|
||||||
outstanding > totalInvoiced * 0.5 ? "at_risk" : "healthy",
|
outstanding > totalInvoiced * 0.5 ? "at_risk" : "healthy",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!contract) {
|
if (!contract) {
|
||||||
return res.status(403).json({ error: "Contract not found or unauthorized" });
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "Contract not found or unauthorized" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateData: any = {};
|
const updateData: any = {};
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
try {
|
try {
|
||||||
if (req.method === "POST") {
|
if (req.method === "POST") {
|
||||||
// Create new invoice
|
// Create new invoice
|
||||||
const {
|
const { description, issue_date, due_date, items, notes, currency } =
|
||||||
description,
|
req.body;
|
||||||
issue_date,
|
|
||||||
due_date,
|
|
||||||
items,
|
|
||||||
notes,
|
|
||||||
currency,
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
if (!due_date || !items || items.length === 0) {
|
if (!due_date || !items || items.length === 0) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
|
|
@ -39,7 +33,10 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate total from items
|
// Calculate total from items
|
||||||
const amountDue = items.reduce((sum: number, item: any) => sum + (item.amount || 0), 0);
|
const amountDue = items.reduce(
|
||||||
|
(sum: number, item: any) => sum + (item.amount || 0),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
// Generate invoice number
|
// Generate invoice number
|
||||||
const { count } = await admin
|
const { count } = await admin
|
||||||
|
|
@ -118,7 +115,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (verifyError || !invoice) {
|
if (verifyError || !invoice) {
|
||||||
return res.status(403).json({ error: "Invoice not found or unauthorized" });
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "Invoice not found or unauthorized" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateData: any = {};
|
const updateData: any = {};
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (verifyError || !member) {
|
if (verifyError || !member) {
|
||||||
return res.status(403).json({ error: "Member not found or unauthorized" });
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "Member not found or unauthorized" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateData: any = {};
|
const updateData: any = {};
|
||||||
|
|
@ -155,7 +157,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!member) {
|
if (!member) {
|
||||||
return res.status(403).json({ error: "Member not found or unauthorized" });
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "Member not found or unauthorized" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { error: deleteError } = await admin
|
const { error: deleteError } = await admin
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (contractError || !contract) {
|
if (contractError || !contract) {
|
||||||
return res.status(403).json({ error: "Contract not found or unauthorized" });
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "Contract not found or unauthorized" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update contract status to active
|
// Update contract status to active
|
||||||
|
|
@ -99,7 +101,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
.eq("user_id", contract.creator_id)
|
.eq("user_id", contract.creator_id)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
const newEarnings = (creatorProfile?.total_earnings || 0) + contract.creator_payout_amount;
|
const newEarnings =
|
||||||
|
(creatorProfile?.total_earnings || 0) + contract.creator_payout_amount;
|
||||||
|
|
||||||
await admin
|
await admin
|
||||||
.from("nexus_creator_profiles")
|
.from("nexus_creator_profiles")
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,9 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
if (oppError || !opportunity || opportunity.posted_by !== user.id) {
|
if (oppError || !opportunity || opportunity.posted_by !== user.id) {
|
||||||
return res
|
return res
|
||||||
.status(403)
|
.status(403)
|
||||||
.json({ error: "Not authorized to post contracts for this opportunity" });
|
.json({
|
||||||
|
error: "Not authorized to post contracts for this opportunity",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the creator has applied
|
// Verify the creator has applied
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
|
|
||||||
if (!account.charges_enabled || !account.payouts_enabled) {
|
if (!account.charges_enabled || !account.payouts_enabled) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
error: "Account setup not complete. Please complete all required steps.",
|
error:
|
||||||
|
"Account setup not complete. Please complete all required steps.",
|
||||||
chargesEnabled: account.charges_enabled,
|
chargesEnabled: account.charges_enabled,
|
||||||
payoutsEnabled: account.payouts_enabled,
|
payoutsEnabled: account.payouts_enabled,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,19 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!sig || !webhookSecret) {
|
if (!sig || !webhookSecret) {
|
||||||
return res.status(400).json({ error: "Missing webhook signature or secret" });
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: "Missing webhook signature or secret" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = typeof req.body === "string" ? req.body : JSON.stringify(req.body);
|
const body =
|
||||||
|
typeof req.body === "string" ? req.body : JSON.stringify(req.body);
|
||||||
event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
|
event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Webhook signature verification failed:", error.message);
|
console.error("Webhook signature verification failed:", error.message);
|
||||||
return res.status(400).json({ error: "Webhook signature verification failed" });
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: "Webhook signature verification failed" });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -60,18 +65,16 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!existingPayment) {
|
if (!existingPayment) {
|
||||||
await admin
|
await admin.from("nexus_payments").insert({
|
||||||
.from("nexus_payments")
|
contract_id: contract.id,
|
||||||
.insert({
|
amount: contract.total_amount,
|
||||||
contract_id: contract.id,
|
creator_payout: contract.creator_payout_amount,
|
||||||
amount: contract.total_amount,
|
aethex_commission: contract.aethex_commission_amount,
|
||||||
creator_payout: contract.creator_payout_amount,
|
payment_method: "stripe",
|
||||||
aethex_commission: contract.aethex_commission_amount,
|
payment_status: "completed",
|
||||||
payment_method: "stripe",
|
payment_date: new Date().toISOString(),
|
||||||
payment_status: "completed",
|
stripe_payment_intent_id: paymentIntent.id,
|
||||||
payment_date: new Date().toISOString(),
|
});
|
||||||
stripe_payment_intent_id: paymentIntent.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update creator earnings
|
// Update creator earnings
|
||||||
|
|
@ -82,7 +85,8 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
const newEarnings =
|
const newEarnings =
|
||||||
(creatorProfile?.total_earnings || 0) + contract.creator_payout_amount;
|
(creatorProfile?.total_earnings || 0) +
|
||||||
|
contract.creator_payout_amount;
|
||||||
|
|
||||||
await admin
|
await admin
|
||||||
.from("nexus_creator_profiles")
|
.from("nexus_creator_profiles")
|
||||||
|
|
@ -123,17 +127,15 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||||
.eq("id", contract.id);
|
.eq("id", contract.id);
|
||||||
|
|
||||||
// Create failed payment record
|
// Create failed payment record
|
||||||
await admin
|
await admin.from("nexus_payments").insert({
|
||||||
.from("nexus_payments")
|
contract_id: contract.id,
|
||||||
.insert({
|
amount: contract.total_amount,
|
||||||
contract_id: contract.id,
|
creator_payout: 0,
|
||||||
amount: contract.total_amount,
|
aethex_commission: 0,
|
||||||
creator_payout: 0,
|
payment_method: "stripe",
|
||||||
aethex_commission: 0,
|
payment_status: "failed",
|
||||||
payment_method: "stripe",
|
stripe_payment_intent_id: paymentIntent.id,
|
||||||
payment_status: "failed",
|
});
|
||||||
stripe_payment_intent_id: paymentIntent.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,13 @@ import { useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { useAethexToast } from "@/hooks/use-aethex-toast";
|
import { useAethexToast } from "@/hooks/use-aethex-toast";
|
||||||
import { Loader2, X } from "lucide-react";
|
import { Loader2, X } from "lucide-react";
|
||||||
|
|
@ -28,7 +34,9 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
||||||
const [excerpt, setExcerpt] = useState(initialData?.excerpt || "");
|
const [excerpt, setExcerpt] = useState(initialData?.excerpt || "");
|
||||||
const [html, setHtml] = useState(initialData?.html || "");
|
const [html, setHtml] = useState(initialData?.html || "");
|
||||||
const [slug, setSlug] = useState(initialData?.slug || "");
|
const [slug, setSlug] = useState(initialData?.slug || "");
|
||||||
const [featureImage, setFeatureImage] = useState(initialData?.feature_image || "");
|
const [featureImage, setFeatureImage] = useState(
|
||||||
|
initialData?.feature_image || "",
|
||||||
|
);
|
||||||
const [tags, setTags] = useState<string[]>(initialData?.tags || []);
|
const [tags, setTags] = useState<string[]>(initialData?.tags || []);
|
||||||
const [tagInput, setTagInput] = useState("");
|
const [tagInput, setTagInput] = useState("");
|
||||||
const [metaTitle, setMetaTitle] = useState(initialData?.meta_title || "");
|
const [metaTitle, setMetaTitle] = useState(initialData?.meta_title || "");
|
||||||
|
|
@ -139,7 +147,8 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
||||||
/>
|
/>
|
||||||
{!slug && title && (
|
{!slug && title && (
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Auto-slug: <code className="bg-background/80 px-2 py-1">{autoSlug}</code>
|
Auto-slug:{" "}
|
||||||
|
<code className="bg-background/80 px-2 py-1">{autoSlug}</code>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -173,7 +182,9 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
||||||
<Input
|
<Input
|
||||||
value={tagInput}
|
value={tagInput}
|
||||||
onChange={(e) => setTagInput(e.target.value)}
|
onChange={(e) => setTagInput(e.target.value)}
|
||||||
onKeyPress={(e) => e.key === "Enter" && (e.preventDefault(), addTag())}
|
onKeyPress={(e) =>
|
||||||
|
e.key === "Enter" && (e.preventDefault(), addTag())
|
||||||
|
}
|
||||||
placeholder="Add tag and press Enter"
|
placeholder="Add tag and press Enter"
|
||||||
className="border-border/50"
|
className="border-border/50"
|
||||||
/>
|
/>
|
||||||
|
|
@ -238,7 +249,7 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
||||||
<Textarea
|
<Textarea
|
||||||
value={html}
|
value={html}
|
||||||
onChange={(e) => setHtml(e.target.value)}
|
onChange={(e) => setHtml(e.target.value)}
|
||||||
placeholder='<p>Write your post content here...</p>'
|
placeholder="<p>Write your post content here...</p>"
|
||||||
className="border-border/50 font-mono h-96"
|
className="border-border/50 font-mono h-96"
|
||||||
/>
|
/>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
|
|
@ -255,7 +266,9 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="prose prose-invert max-w-none">
|
<CardContent className="prose prose-invert max-w-none">
|
||||||
{title && <h1>{title}</h1>}
|
{title && <h1>{title}</h1>}
|
||||||
{excerpt && <p className="text-muted-foreground italic">{excerpt}</p>}
|
{excerpt && (
|
||||||
|
<p className="text-muted-foreground italic">{excerpt}</p>
|
||||||
|
)}
|
||||||
<div dangerouslySetInnerHTML={{ __html: html }} />
|
<div dangerouslySetInnerHTML={{ __html: html }} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,10 @@ export default function AdminBlogManager() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<Label htmlFor="filter-category" className="text-xs mb-1 block">
|
<Label
|
||||||
|
htmlFor="filter-category"
|
||||||
|
className="text-xs mb-1 block"
|
||||||
|
>
|
||||||
Category
|
Category
|
||||||
</Label>
|
</Label>
|
||||||
<select
|
<select
|
||||||
|
|
@ -441,7 +444,9 @@ export default function AdminBlogManager() {
|
||||||
{!slug && title && (
|
{!slug && title && (
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Auto-slug:{" "}
|
Auto-slug:{" "}
|
||||||
<code className="bg-background/80 px-2 py-1">{autoSlug}</code>
|
<code className="bg-background/80 px-2 py-1">
|
||||||
|
{autoSlug}
|
||||||
|
</code>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import {
|
import { Zap, Building2, ArrowRight } from "lucide-react";
|
||||||
Zap,
|
|
||||||
Building2,
|
|
||||||
ArrowRight,
|
|
||||||
} from "lucide-react";
|
|
||||||
|
|
||||||
interface BlogCTAProps {
|
interface BlogCTAProps {
|
||||||
variant?: "nexus" | "corp" | "both";
|
variant?: "nexus" | "corp" | "both";
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ export async function fetchGhostPosts(
|
||||||
limit: String(limit),
|
limit: String(limit),
|
||||||
page: String(page),
|
page: String(page),
|
||||||
include: "authors,tags",
|
include: "authors,tags",
|
||||||
fields: "id,title,slug,excerpt,html,feature_image,published_at,reading_time,authors,tags",
|
fields:
|
||||||
|
"id,title,slug,excerpt,html,feature_image,published_at,reading_time,authors,tags",
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = `${GHOST_API_URL}/ghost/api/content/posts/?${params.toString()}`;
|
const url = `${GHOST_API_URL}/ghost/api/content/posts/?${params.toString()}`;
|
||||||
|
|
@ -72,7 +73,8 @@ export async function fetchGhostPostBySlug(
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
key: GHOST_CONTENT_API_KEY,
|
key: GHOST_CONTENT_API_KEY,
|
||||||
include: "authors,tags",
|
include: "authors,tags",
|
||||||
fields: "id,title,slug,excerpt,html,feature_image,published_at,reading_time,authors,tags",
|
fields:
|
||||||
|
"id,title,slug,excerpt,html,feature_image,published_at,reading_time,authors,tags",
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = `${GHOST_API_URL}/ghost/api/content/posts/slug/${encodeURIComponent(slug)}/?${params.toString()}`;
|
const url = `${GHOST_API_URL}/ghost/api/content/posts/slug/${encodeURIComponent(slug)}/?${params.toString()}`;
|
||||||
|
|
@ -110,10 +112,7 @@ export function transformGhostPost(post: GhostPost) {
|
||||||
})
|
})
|
||||||
: "",
|
: "",
|
||||||
readTime: post.reading_time || null,
|
readTime: post.reading_time || null,
|
||||||
category:
|
category: post.tags && post.tags.length > 0 ? post.tags[0].name : "General",
|
||||||
post.tags && post.tags.length > 0
|
|
||||||
? post.tags[0].name
|
|
||||||
: "General",
|
|
||||||
image: post.feature_image || null,
|
image: post.feature_image || null,
|
||||||
trending: false,
|
trending: false,
|
||||||
likes: null,
|
likes: null,
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,12 @@ function generateGhostJWT(): string {
|
||||||
aud: "/admin/",
|
aud: "/admin/",
|
||||||
};
|
};
|
||||||
|
|
||||||
const headerEncoded = Buffer.from(JSON.stringify(header)).toString("base64url");
|
const headerEncoded = Buffer.from(JSON.stringify(header)).toString(
|
||||||
const payloadEncoded = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
"base64url",
|
||||||
|
);
|
||||||
|
const payloadEncoded = Buffer.from(JSON.stringify(payload)).toString(
|
||||||
|
"base64url",
|
||||||
|
);
|
||||||
|
|
||||||
const signatureInput = `${headerEncoded}.${payloadEncoded}`;
|
const signatureInput = `${headerEncoded}.${payloadEncoded}`;
|
||||||
const signature = crypto
|
const signature = crypto
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,7 @@
|
||||||
"permanent": true
|
"permanent": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rewrites": [
|
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }],
|
||||||
{ "source": "/(.*)", "destination": "/index.html" }
|
|
||||||
],
|
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
"source": "/assets/(.*)",
|
"source": "/assets/(.*)",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue