@seo-console/package 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +281 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SEO Console Package
|
|
2
2
|
|
|
3
|
-
A production-ready SEO validation and management system for Next.js applications.
|
|
3
|
+
A production-ready SEO validation and management system for Next.js applications. This package provides a complete admin interface for managing SEO metadata, accessible through a new tab in your admin section.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,78 +8,322 @@ A production-ready SEO validation and management system for Next.js applications
|
|
|
8
8
|
npm install @seo-console/package
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Setup Guide
|
|
12
12
|
|
|
13
|
-
### 1
|
|
13
|
+
### Step 1: Configure Storage (Optional)
|
|
14
14
|
|
|
15
|
-
The package
|
|
15
|
+
The package uses **file storage by default** (no database required). SEO records are stored in `seo-records.json`.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
File storage is the default option. SEO records are stored in a JSON file (`seo-records.json` by default).
|
|
20
|
-
|
|
21
|
-
No configuration needed! The package will automatically use file storage if no Supabase credentials are provided.
|
|
22
|
-
|
|
23
|
-
To customize the file path, set an environment variable:
|
|
17
|
+
To customize the storage location, add to your `.env.local`:
|
|
24
18
|
|
|
25
19
|
```env
|
|
26
20
|
SEO_CONSOLE_STORAGE_PATH=./data/seo-records.json
|
|
27
21
|
```
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
If you prefer using Supabase as your storage backend:
|
|
32
|
-
|
|
33
|
-
1. Create a Supabase project at [supabase.com](https://supabase.com)
|
|
34
|
-
2. Run the database migrations from `migrations/`:
|
|
35
|
-
- `001_initial_schema.sql` - User profiles
|
|
36
|
-
- `002_seo_records_schema.sql` - SEO records table
|
|
37
|
-
3. Add environment variables:
|
|
23
|
+
**Optional:** If you prefer Supabase, set these environment variables:
|
|
38
24
|
|
|
39
25
|
```env
|
|
40
26
|
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
|
41
27
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
|
42
28
|
```
|
|
43
29
|
|
|
44
|
-
The package will automatically detect
|
|
30
|
+
The package will automatically detect and use Supabase if these are set.
|
|
31
|
+
|
|
32
|
+
### Step 2: Create API Routes (REQUIRED - This is why you're getting 404 errors!)
|
|
45
33
|
|
|
46
|
-
|
|
34
|
+
**⚠️ CRITICAL:** The package does NOT include API routes. You MUST create them in your Next.js app. The 404 error you're seeing means the API route doesn't exist yet.
|
|
47
35
|
|
|
48
|
-
|
|
36
|
+
**Create `app/api/seo-records/route.ts`:**
|
|
49
37
|
|
|
50
38
|
```typescript
|
|
51
|
-
|
|
52
|
-
import {
|
|
39
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
40
|
+
import { detectStorageConfig, createStorageAdapter } from "@seo-console/package";
|
|
41
|
+
import { createSEORecordSchema } from "@seo-console/package/server";
|
|
42
|
+
|
|
43
|
+
// GET - Fetch all SEO records
|
|
44
|
+
export async function GET() {
|
|
45
|
+
try {
|
|
46
|
+
// Auto-detect storage type (file or Supabase)
|
|
47
|
+
const config = detectStorageConfig();
|
|
48
|
+
const storage = createStorageAdapter(config);
|
|
49
|
+
|
|
50
|
+
// Check if storage is available
|
|
51
|
+
const isAvailable = await storage.isAvailable();
|
|
52
|
+
if (!isAvailable) {
|
|
53
|
+
return NextResponse.json(
|
|
54
|
+
{ error: "Storage not available" },
|
|
55
|
+
{ status: 500 }
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const records = await storage.getRecords();
|
|
60
|
+
return NextResponse.json({ data: records || [] });
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error("Error fetching SEO records:", error);
|
|
63
|
+
return NextResponse.json(
|
|
64
|
+
{ error: error instanceof Error ? error.message : "Failed to fetch records" },
|
|
65
|
+
{ status: 500 }
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
53
69
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
// POST - Create a new SEO record
|
|
71
|
+
export async function POST(request: NextRequest) {
|
|
72
|
+
try {
|
|
73
|
+
const body = await request.json();
|
|
74
|
+
|
|
75
|
+
// Validate the request body
|
|
76
|
+
const validated = createSEORecordSchema.parse(body);
|
|
77
|
+
|
|
78
|
+
// Auto-detect storage type
|
|
79
|
+
const config = detectStorageConfig();
|
|
80
|
+
const storage = createStorageAdapter(config);
|
|
81
|
+
|
|
82
|
+
const record = await storage.createRecord(validated);
|
|
83
|
+
return NextResponse.json({ data: record }, { status: 201 });
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error("Error creating SEO record:", error);
|
|
86
|
+
if (error instanceof Error) {
|
|
87
|
+
return NextResponse.json(
|
|
88
|
+
{ error: error.message },
|
|
89
|
+
{ status: 400 }
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
return NextResponse.json(
|
|
93
|
+
{ error: "Invalid request" },
|
|
94
|
+
{ status: 400 }
|
|
95
|
+
);
|
|
96
|
+
}
|
|
59
97
|
}
|
|
60
98
|
```
|
|
61
99
|
|
|
62
|
-
|
|
100
|
+
**Create `app/api/seo-records/[id]/route.ts`:**
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
104
|
+
import { detectStorageConfig, createStorageAdapter } from "@seo-console/package";
|
|
105
|
+
import { updateSEORecordSchema } from "@seo-console/package/server";
|
|
106
|
+
|
|
107
|
+
interface RouteParams {
|
|
108
|
+
params: Promise<{ id: string }>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// GET - Get a single SEO record
|
|
112
|
+
export async function GET(
|
|
113
|
+
request: NextRequest,
|
|
114
|
+
{ params }: RouteParams
|
|
115
|
+
) {
|
|
116
|
+
try {
|
|
117
|
+
const { id } = await params;
|
|
118
|
+
const config = detectStorageConfig();
|
|
119
|
+
const storage = createStorageAdapter(config);
|
|
120
|
+
|
|
121
|
+
const record = await storage.getRecordById(id);
|
|
122
|
+
|
|
123
|
+
if (!record) {
|
|
124
|
+
return NextResponse.json(
|
|
125
|
+
{ error: "Record not found" },
|
|
126
|
+
{ status: 404 }
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return NextResponse.json({ data: record });
|
|
131
|
+
} catch (error) {
|
|
132
|
+
return NextResponse.json(
|
|
133
|
+
{ error: error instanceof Error ? error.message : "Failed to fetch record" },
|
|
134
|
+
{ status: 500 }
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// PATCH - Update an SEO record
|
|
140
|
+
export async function PATCH(
|
|
141
|
+
request: NextRequest,
|
|
142
|
+
{ params }: RouteParams
|
|
143
|
+
) {
|
|
144
|
+
try {
|
|
145
|
+
const { id } = await params;
|
|
146
|
+
const body = await request.json();
|
|
147
|
+
|
|
148
|
+
const validated = updateSEORecordSchema.parse({ ...body, id });
|
|
149
|
+
const config = detectStorageConfig();
|
|
150
|
+
const storage = createStorageAdapter(config);
|
|
151
|
+
|
|
152
|
+
const record = await storage.updateRecord(validated);
|
|
153
|
+
return NextResponse.json({ data: record });
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (error instanceof Error) {
|
|
156
|
+
return NextResponse.json(
|
|
157
|
+
{ error: error.message },
|
|
158
|
+
{ status: 400 }
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
return NextResponse.json(
|
|
162
|
+
{ error: "Invalid request" },
|
|
163
|
+
{ status: 400 }
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// DELETE - Delete an SEO record
|
|
169
|
+
export async function DELETE(
|
|
170
|
+
request: NextRequest,
|
|
171
|
+
{ params }: RouteParams
|
|
172
|
+
) {
|
|
173
|
+
try {
|
|
174
|
+
const { id } = await params;
|
|
175
|
+
const config = detectStorageConfig();
|
|
176
|
+
const storage = createStorageAdapter(config);
|
|
177
|
+
|
|
178
|
+
await storage.deleteRecord(id);
|
|
179
|
+
return NextResponse.json({ success: true });
|
|
180
|
+
} catch (error) {
|
|
181
|
+
return NextResponse.json(
|
|
182
|
+
{ error: error instanceof Error ? error.message : "Failed to delete record" },
|
|
183
|
+
{ status: 500 }
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
> **Important:** These API routes use the storage adapter system, which automatically works with:
|
|
190
|
+
> - **File storage** (default) - if no Supabase credentials are set
|
|
191
|
+
> - **Supabase** - if `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` are set
|
|
192
|
+
|
|
193
|
+
### Step 3: Add Admin Pages
|
|
194
|
+
|
|
195
|
+
Create the admin SEO section in your Next.js app. This will be accessible as a new tab in your admin area.
|
|
196
|
+
|
|
197
|
+
**Create the directory structure:**
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
app/admin/seo/
|
|
201
|
+
├── page.tsx (Main dashboard)
|
|
202
|
+
├── editor/
|
|
203
|
+
│ └── page.tsx (SEO record editor)
|
|
204
|
+
├── reports/
|
|
205
|
+
│ └── page.tsx (Reports & analytics)
|
|
206
|
+
├── search/
|
|
207
|
+
│ └── page.tsx (Search & validation)
|
|
208
|
+
└── settings/
|
|
209
|
+
└── page.tsx (Settings)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**`app/admin/seo/page.tsx` (Main Dashboard):**
|
|
63
213
|
|
|
64
214
|
```typescript
|
|
65
|
-
// app/admin/seo/page.tsx
|
|
66
215
|
"use client";
|
|
67
216
|
|
|
68
|
-
import {
|
|
69
|
-
import {
|
|
217
|
+
import { useState, useEffect } from "react";
|
|
218
|
+
import { useRouter } from "next/navigation";
|
|
219
|
+
import type { SEORecord } from "@seo-console/package";
|
|
70
220
|
|
|
71
221
|
export default function SEOAdminPage() {
|
|
222
|
+
const router = useRouter();
|
|
223
|
+
const [records, setRecords] = useState<SEORecord[]>([]);
|
|
224
|
+
const [loading, setLoading] = useState(true);
|
|
225
|
+
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
fetch("/api/seo-records")
|
|
228
|
+
.then((res) => res.json())
|
|
229
|
+
.then((data) => {
|
|
230
|
+
setRecords(data.data || []);
|
|
231
|
+
setLoading(false);
|
|
232
|
+
})
|
|
233
|
+
.catch(() => setLoading(false));
|
|
234
|
+
}, []);
|
|
235
|
+
|
|
72
236
|
return (
|
|
73
237
|
<div>
|
|
74
238
|
<h1>SEO Management</h1>
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
239
|
+
<nav>
|
|
240
|
+
<a href="/admin/seo">Dashboard</a>
|
|
241
|
+
<a href="/admin/seo/editor">Editor</a>
|
|
242
|
+
<a href="/admin/seo/reports">Reports</a>
|
|
243
|
+
<a href="/admin/seo/search">Search</a>
|
|
244
|
+
<a href="/admin/seo/settings">Settings</a>
|
|
245
|
+
</nav>
|
|
246
|
+
{/* Your SEO dashboard content */}
|
|
78
247
|
</div>
|
|
79
248
|
);
|
|
80
249
|
}
|
|
81
250
|
```
|
|
82
251
|
|
|
252
|
+
**`app/admin/seo/editor/page.tsx` (Editor):**
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
"use client";
|
|
256
|
+
|
|
257
|
+
import { useState, useEffect } from "react";
|
|
258
|
+
import { useSearchParams } from "next/navigation";
|
|
259
|
+
import type { SEORecord } from "@seo-console/package";
|
|
260
|
+
|
|
261
|
+
export default function EditorPage() {
|
|
262
|
+
const searchParams = useSearchParams();
|
|
263
|
+
const route = searchParams.get("route");
|
|
264
|
+
const [record, setRecord] = useState<SEORecord | null>(null);
|
|
265
|
+
const [formData, setFormData] = useState({
|
|
266
|
+
title: "",
|
|
267
|
+
description: "",
|
|
268
|
+
canonicalUrl: "",
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Load and save logic here
|
|
272
|
+
return (
|
|
273
|
+
<div>
|
|
274
|
+
<h1>Edit SEO Record</h1>
|
|
275
|
+
{/* Your editor form */}
|
|
276
|
+
</div>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
> **Note:** For complete implementation examples, see the demo app in the repository. The package provides the data layer and utilities; you'll need to build the UI components or copy from the demo.
|
|
282
|
+
|
|
283
|
+
### Step 4: Add SEO Metadata to Your Pages
|
|
284
|
+
|
|
285
|
+
Use the `useGenerateMetadata` hook to automatically generate metadata from your SEO records:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
// app/blog/[slug]/page.tsx
|
|
289
|
+
import { useGenerateMetadata } from "@seo-console/package/hooks";
|
|
290
|
+
|
|
291
|
+
export async function generateMetadata({ params }: { params: { slug: string } }) {
|
|
292
|
+
const metadata = await useGenerateMetadata({
|
|
293
|
+
routePath: `/blog/${params.slug}`,
|
|
294
|
+
fallback: {
|
|
295
|
+
title: "Blog Post",
|
|
296
|
+
description: "Default description"
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
return metadata;
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Step 5: Add to Your Admin Navigation
|
|
304
|
+
|
|
305
|
+
Add a link to the SEO admin section in your main admin navigation:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
// app/admin/layout.tsx or your admin navigation component
|
|
309
|
+
<nav>
|
|
310
|
+
<Link href="/admin/dashboard">Dashboard</Link>
|
|
311
|
+
<Link href="/admin/seo">SEO</Link> {/* Add this */}
|
|
312
|
+
<Link href="/admin/users">Users</Link>
|
|
313
|
+
{/* ... other admin links */}
|
|
314
|
+
</nav>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Quick Start Summary
|
|
318
|
+
|
|
319
|
+
1. **Install:** `npm install @seo-console/package`
|
|
320
|
+
2. **Create API route:** `app/api/seo-records/route.ts` (see Step 2)
|
|
321
|
+
3. **Create admin pages:** `app/admin/seo/` directory with pages (see Step 3)
|
|
322
|
+
4. **Add to navigation:** Link to `/admin/seo` in your admin menu
|
|
323
|
+
5. **Use in pages:** Add `generateMetadata` to your pages (see Step 4)
|
|
324
|
+
|
|
325
|
+
That's it! The SEO admin interface will be accessible at `/admin/seo` as a new tab in your admin area.
|
|
326
|
+
|
|
83
327
|
## API Reference
|
|
84
328
|
|
|
85
329
|
### Hooks
|
|
@@ -100,28 +344,8 @@ const metadata = await useGenerateMetadata({
|
|
|
100
344
|
});
|
|
101
345
|
```
|
|
102
346
|
|
|
103
|
-
### Components
|
|
104
|
-
|
|
105
|
-
#### `SEORecordList`
|
|
106
|
-
|
|
107
|
-
Displays a list of all SEO records with edit/delete functionality.
|
|
108
|
-
|
|
109
|
-
#### `SEORecordForm`
|
|
110
|
-
|
|
111
|
-
Form for creating and editing SEO records.
|
|
112
|
-
|
|
113
|
-
#### `ValidationDashboard`
|
|
114
|
-
|
|
115
|
-
Dashboard showing validation results for all SEO records.
|
|
116
|
-
|
|
117
|
-
#### `OGImagePreview`
|
|
118
|
-
|
|
119
|
-
Preview component showing how OG images appear on social platforms.
|
|
120
|
-
|
|
121
347
|
### Server-Side Functions
|
|
122
348
|
|
|
123
|
-
The package exports server-side functions for API routes and server components:
|
|
124
|
-
|
|
125
349
|
```typescript
|
|
126
350
|
import {
|
|
127
351
|
getSEORecords,
|