-
Notifications
You must be signed in to change notification settings - Fork 951
refresh #502
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
refresh #502
Changes from 2 commits
3d9aa4e
701839b
665161a
d4d51bd
d363124
12ba913
315192d
41ce1a6
01ee336
6e41034
d53e227
ae55956
4a708f7
871118f
b8afd00
8e065ac
1591daf
626397b
8203765
2d8e327
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import type {NextConfig} from 'next'; | ||
|
|
||
| const nextConfig: NextConfig = { | ||
| typescript: { | ||
| ignoreBuildErrors: true, | ||
| }, | ||
| eslint: { | ||
| ignoreDuringBuilds: true, | ||
| }, | ||
| images: { | ||
| remotePatterns: [ | ||
| { | ||
| protocol: 'https', | ||
| hostname: 'placehold.co', | ||
| port: '', | ||
| pathname: '/**', | ||
| }, | ||
| { | ||
| protocol: 'https', | ||
| hostname: 'images.unsplash.com', | ||
| port: '', | ||
| pathname: '/**', | ||
| }, | ||
| { | ||
| protocol: 'https', | ||
| hostname: 'picsum.photos', | ||
| port: '', | ||
| pathname: '/**', | ||
| }, | ||
| ], | ||
| }, | ||
| }; | ||
|
|
||
| export default nextConfig; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import { config } from 'dotenv'; | ||
| config(); | ||
|
|
||
| import '@/ai/flows/analyze-photo-for-content-suggestions.ts'; | ||
| import '@/ai/flows/improve-existing-post.ts'; | ||
| import '@/ai/flows/generate-content-from-comment.ts'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| 'use server'; | ||
| /** | ||
| * @fileOverview Analyzes a photo to suggest relevant topics and content ideas for a WordPress post. | ||
| * | ||
| * - analyzePhotoForContentSuggestions - A function that handles the photo analysis and content suggestion process. | ||
| * - AnalyzePhotoForContentSuggestionsInput - The input type for the analyzePhotoForContentSuggestions function. | ||
| * - AnalyzePhotoForContentSuggestionsOutput - The return type for the analyzePhotoForContentSuggestions function. | ||
| */ | ||
|
|
||
| import {ai} from '@/ai/genkit'; | ||
| import {z} from 'genkit'; | ||
|
|
||
| const AnalyzePhotoForContentSuggestionsInputSchema = z.object({ | ||
| photoDataUri: z | ||
| .string() | ||
| .describe( | ||
| "A photo, as a data URI that must include a MIME type and use Base64 encoding. Expected format: 'data:<mimetype>;base64,<encoded_data>'." | ||
| ), | ||
| }); | ||
| export type AnalyzePhotoForContentSuggestionsInput = z.infer<typeof AnalyzePhotoForContentSuggestionsInputSchema>; | ||
|
|
||
| const AnalyzePhotoForContentSuggestionsOutputSchema = z.object({ | ||
| suggestedTopics: z.array(z.string()).describe('A list of suggested topics for a WordPress post based on the photo.'), | ||
| contentIdeas: z.array(z.string()).describe('A list of content ideas for a WordPress post based on the photo.'), | ||
| }); | ||
| export type AnalyzePhotoForContentSuggestionsOutput = z.infer<typeof AnalyzePhotoForContentSuggestionsOutputSchema>; | ||
|
|
||
| export async function analyzePhotoForContentSuggestions(input: AnalyzePhotoForContentSuggestionsInput): Promise<AnalyzePhotoForContentSuggestionsOutput> { | ||
| return analyzePhotoForContentSuggestionsFlow(input); | ||
| } | ||
|
|
||
| const prompt = ai.definePrompt({ | ||
| name: 'analyzePhotoForContentSuggestionsPrompt', | ||
| input: {schema: AnalyzePhotoForContentSuggestionsInputSchema}, | ||
| output: {schema: AnalyzePhotoForContentSuggestionsOutputSchema}, | ||
| prompt: `You are an expert content strategist for WordPress. You will analyze the photo provided and suggest relevant topics and content ideas for a WordPress post. | ||
|
|
||
| Photo: {{media url=photoDataUri}} | ||
|
|
||
| Please provide the suggested topics and content ideas in a JSON format. | ||
| `, | ||
| }); | ||
|
|
||
| const analyzePhotoForContentSuggestionsFlow = ai.defineFlow( | ||
| { | ||
| name: 'analyzePhotoForContentSuggestionsFlow', | ||
| inputSchema: AnalyzePhotoForContentSuggestionsInputSchema, | ||
| outputSchema: AnalyzePhotoForContentSuggestionsOutputSchema, | ||
| }, | ||
| async input => { | ||
| const {output} = await prompt(input); | ||
| return output!; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a non-null assertion ( if (!output) {
throw new Error('Failed to get a response from the AI model.');
}
return output; |
||
| } | ||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| // src/ai/flows/generate-content-from-comment.ts | ||
| 'use server'; | ||
| /** | ||
| * @fileOverview Generates WordPress content (title, body, tags) from a user comment. | ||
| * | ||
| * - generateContentFromComment - A function that generates WordPress content based on a comment. | ||
| * - GenerateContentFromCommentInput - The input type for the generateContentFromComment function. | ||
| * - GenerateContentFromCommentOutput - The return type for the generateContentFromComment function. | ||
| */ | ||
|
|
||
| import {ai} from '@/ai/genkit'; | ||
| import {z} from 'genkit'; | ||
|
|
||
| const GenerateContentFromCommentInputSchema = z.object({ | ||
| comment: z.string().describe('The comment to generate content from.'), | ||
| }); | ||
| export type GenerateContentFromCommentInput = z.infer<typeof GenerateContentFromCommentInputSchema>; | ||
|
|
||
| const GenerateContentFromCommentOutputSchema = z.object({ | ||
| title: z.string().describe('The title of the WordPress post.'), | ||
| body: z.string().describe('The body of the WordPress post.'), | ||
| tags: z.array(z.string()).describe('Suggested tags for the WordPress post.'), | ||
| }); | ||
| export type GenerateContentFromCommentOutput = z.infer<typeof GenerateContentFromCommentOutputSchema>; | ||
|
|
||
| export async function generateContentFromComment(input: GenerateContentFromCommentInput): Promise<GenerateContentFromCommentOutput> { | ||
| return generateContentFromCommentFlow(input); | ||
| } | ||
|
|
||
| const prompt = ai.definePrompt({ | ||
| name: 'generateContentFromCommentPrompt', | ||
| input: {schema: GenerateContentFromCommentInputSchema}, | ||
| output: {schema: GenerateContentFromCommentOutputSchema}, | ||
| prompt: `You are an AI assistant that generates WordPress content based on user comments. | ||
|
|
||
| Based on the following comment, generate a title, body, and suggested tags for a WordPress post. | ||
|
|
||
| Comment: {{{comment}}} | ||
|
|
||
| The title should be concise and engaging. | ||
| The body should be informative and well-written. | ||
| The tags should be relevant and helpful for SEO. | ||
|
|
||
| Ensure that the title, body and tags are appropriate and professional. | ||
| `,config: { | ||
| safetySettings: [ | ||
| { | ||
| category: 'HARM_CATEGORY_HATE_SPEECH', | ||
| threshold: 'BLOCK_ONLY_HIGH', | ||
| }, | ||
| { | ||
| category: 'HARM_CATEGORY_DANGEROUS_CONTENT', | ||
| threshold: 'BLOCK_NONE', | ||
| }, | ||
|
Comment on lines
+51
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The safety setting for |
||
| { | ||
| category: 'HARM_CATEGORY_HARASSMENT', | ||
| threshold: 'BLOCK_MEDIUM_AND_ABOVE', | ||
| }, | ||
| { | ||
| category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', | ||
| threshold: 'BLOCK_LOW_AND_ABOVE', | ||
| }, | ||
| ], | ||
| }, | ||
| }); | ||
|
|
||
| const generateContentFromCommentFlow = ai.defineFlow( | ||
| { | ||
| name: 'generateContentFromCommentFlow', | ||
| inputSchema: GenerateContentFromCommentInputSchema, | ||
| outputSchema: GenerateContentFromCommentOutputSchema, | ||
| }, | ||
| async input => { | ||
| const {output} = await prompt(input); | ||
| return output!; | ||
| } | ||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| 'use server'; | ||
|
|
||
| /** | ||
| * @fileOverview Rewrites an existing blog post to match a desired style, generating a new title and tags. | ||
| * | ||
| * - improveExistingPost - A function that handles the rewriting process. | ||
| * - ImproveExistingPostInput - The input type for the improveExistingPost function. | ||
| * - ImproveExistingPostOutput - The return type for the improveExistingPost function. | ||
| */ | ||
|
|
||
| import {ai} from '@/ai/genkit'; | ||
| import {z} from 'genkit'; | ||
|
|
||
| const ImproveExistingPostInputSchema = z.object({ | ||
| existingPost: z.string().describe('The existing blog post content.'), | ||
| desiredStyle: z.string().describe('The desired style for the blog post.'), | ||
| }); | ||
| export type ImproveExistingPostInput = z.infer<typeof ImproveExistingPostInputSchema>; | ||
|
|
||
| const ImproveExistingPostOutputSchema = z.object({ | ||
| title: z.string().describe('The new, improved title for the blog post.'), | ||
| body: z.string().describe('The rewritten blog post content.'), | ||
| tags: z.array(z.string()).describe('A list of relevant tags for the rewritten post.'), | ||
| }); | ||
| export type ImproveExistingPostOutput = z.infer<typeof ImproveExistingPostOutputSchema>; | ||
|
|
||
| export async function improveExistingPost(input: ImproveExistingPostInput): Promise<ImproveExistingPostOutput> { | ||
| return improveExistingPostFlow(input); | ||
| } | ||
|
|
||
| const prompt = ai.definePrompt({ | ||
| name: 'improveExistingPostPrompt', | ||
| input: {schema: ImproveExistingPostInputSchema}, | ||
| output: {schema: ImproveExistingPostOutputSchema}, | ||
| prompt: `You are an expert blog post writer. You will rewrite the existing blog post to match the desired style. | ||
| You must also generate a new, catchy title for the post and a list of relevant tags. | ||
|
|
||
| Existing Blog Post: | ||
| {{{existingPost}}} | ||
|
|
||
| Desired Style: | ||
| {{{desiredStyle}}} | ||
|
|
||
| Respond with the rewritten post body, a new title, and new tags in the specified JSON format.`, | ||
| config: { | ||
| safetySettings: [ | ||
| { | ||
| category: 'HARM_CATEGORY_HATE_SPEECH', | ||
| threshold: 'BLOCK_ONLY_HIGH', | ||
| }, | ||
| { | ||
| category: 'HARM_CATEGORY_DANGEROUS_CONTENT', | ||
| threshold: 'BLOCK_NONE', | ||
| }, | ||
| { | ||
| category: 'HARM_CATEGORY_HARASSMENT', | ||
| threshold: 'BLOCK_MEDIUM_AND_ABOVE', | ||
| }, | ||
| { | ||
| category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', | ||
| threshold: 'BLOCK_LOW_AND_ABOVE', | ||
| }, | ||
| ], | ||
| }, | ||
| }); | ||
|
|
||
| const improveExistingPostFlow = ai.defineFlow( | ||
| { | ||
| name: 'improveExistingPostFlow', | ||
| inputSchema: ImproveExistingPostInputSchema, | ||
| outputSchema: ImproveExistingPostOutputSchema, | ||
| }, | ||
| async input => { | ||
| const {output} = await prompt(input); | ||
| return output!; | ||
| } | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import {genkit} from 'genkit'; | ||
| import {googleAI} from '@genkit-ai/google-genai'; | ||
|
|
||
| export const ai = genkit({ | ||
| plugins: [googleAI()], | ||
| model: 'googleai/gemini-2.5-flash', | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { ReactNode } from 'react'; | ||
|
|
||
| export default function AdminLayout({ children }: { children: ReactNode }) { | ||
| return <div className="min-h-screen bg-background text-foreground">{children}</div>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| 'use client'; | ||
|
|
||
| import { ArrowLeft, Shield, UserCog } from 'lucide-react'; | ||
| import Link from 'next/link'; | ||
| import { Button } from '@/components/ui/button'; | ||
| import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; | ||
|
|
||
| export default function AdminPage() { | ||
| return ( | ||
| <div className="container mx-auto py-8"> | ||
| <div className="flex items-center justify-between mb-6"> | ||
| <div className="flex items-center gap-4"> | ||
| <Shield className="h-8 w-8 text-primary" /> | ||
| <h1 className="text-3xl font-bold">User Management</h1> | ||
| </div> | ||
| <Button asChild variant="outline"> | ||
| <Link href="/"> | ||
| <ArrowLeft className="mr-2 h-4 w-4" /> | ||
| Back to App | ||
| </Link> | ||
| </Button> | ||
| </div> | ||
|
|
||
| <Card> | ||
| <CardHeader> | ||
| <CardTitle className="flex items-center gap-2"> | ||
| <UserCog className="h-6 w-6" /> | ||
| Admin Panel Functionality | ||
| </CardTitle> | ||
| </CardHeader> | ||
| <CardContent className="space-y-4"> | ||
| <p className="text-muted-foreground"> | ||
| This page is a placeholder for user management functionality. Listing all users from the client is disabled by default for security reasons. | ||
| </p> | ||
| <div className="rounded-lg border border-blue-500/50 bg-blue-50 p-4 text-blue-900 dark:bg-blue-950 dark:text-blue-200"> | ||
| <h3 className="font-semibold">How to Implement This Securely</h3> | ||
| <p className="mt-2 text-sm"> | ||
| To build a secure admin panel, you should use Firebase's server-side features: | ||
| </p> | ||
| <ol className="mt-2 list-decimal pl-5 text-sm space-y-1"> | ||
| <li> | ||
| <strong>Custom Claims:</strong> Assign an `admin: true` custom claim to trusted users using the Firebase Admin SDK (typically in a Cloud Function). | ||
| </li> | ||
| <li> | ||
| <strong>Security Rules:</strong> Update your Firestore security rules to only allow users with the `admin: true` claim to list all documents in the `/users` collection. | ||
| </li> | ||
| <li> | ||
| <strong>Backend Logic:</strong> For advanced user management (like deleting users or changing roles), create secure Cloud Functions that can be called from this admin panel. | ||
| </li> | ||
| </ol> | ||
| </div> | ||
| <p className="text-sm text-muted-foreground"> | ||
| This ensures that only authorized administrators can view and manage user data, protecting the privacy of your users. | ||
| </p> | ||
| </CardContent> | ||
| </Card> | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disabling TypeScript and ESLint checks during the build process is highly discouraged for production applications. This can lead to deploying code with type errors or linting issues, which can cause runtime errors and make the codebase harder to maintain. It's recommended to enable these checks to ensure code quality and stability.