This commit is contained in:
Josh 2026-01-09 00:24:04 +02:00
commit 91a950e72f
17 changed files with 2724 additions and 0 deletions

423
templates.go Normal file
View file

@ -0,0 +1,423 @@
package main
// HookInfo describes a plugin hook
type HookInfo struct {
Name string `json:"name"`
Label string `json:"label"`
Description string `json:"description"`
Pattern string `json:"pattern"` // event, validation, transform
TestData map[string]any `json:"test_data"`
}
// Available hooks
var hooks = []HookInfo{
{
Name: "post.published",
Label: "Post Published",
Description: "Triggered when a post is published",
Pattern: "event",
TestData: map[string]any{
"post": map[string]any{
"slug": "hello-world",
"title": "Hello World",
"url": "/hello-world",
"excerpt": "This is a test post for plugin development.",
"publishedAt": "2024-01-15T10:30:00Z",
"tags": []string{"test", "development"},
"readingTime": 3,
},
"author": map[string]any{
"name": "Test Author",
"email": "author@example.com",
},
"blog": map[string]any{
"name": "Test Blog",
"url": "https://test.writekit.dev",
},
},
},
{
Name: "post.updated",
Label: "Post Updated",
Description: "Triggered when a post is updated",
Pattern: "event",
TestData: map[string]any{
"post": map[string]any{
"slug": "hello-world",
"title": "Hello World (Updated)",
"url": "/hello-world",
"excerpt": "This is a test post that was updated.",
"publishedAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-16T14:00:00Z",
"tags": []string{"test", "development", "updated"},
},
"author": map[string]any{
"name": "Test Author",
"email": "author@example.com",
},
"changes": map[string]any{
"title": map[string]any{"old": "Hello World", "new": "Hello World (Updated)"},
"content": true,
"tags": map[string]any{"added": []string{"updated"}, "removed": []string{}},
},
},
},
{
Name: "comment.validate",
Label: "Comment Validate",
Description: "Validate comments before creation",
Pattern: "validation",
TestData: map[string]any{
"content": "This is a test comment for moderation.",
"authorName": "Test User",
"authorEmail": "user@example.com",
"postSlug": "hello-world",
},
},
{
Name: "comment.created",
Label: "Comment Created",
Description: "Triggered when a comment is created",
Pattern: "event",
TestData: map[string]any{
"comment": map[string]any{
"id": "test-comment-123",
"content": "Great post! Thanks for sharing.",
"authorName": "Test User",
"authorEmail": "user@example.com",
"postSlug": "hello-world",
"createdAt": "2024-01-15T12:00:00Z",
},
"post": map[string]any{
"slug": "hello-world",
"title": "Hello World",
"url": "/hello-world",
},
},
},
{
Name: "member.subscribed",
Label: "Member Subscribed",
Description: "Triggered when a member subscribes",
Pattern: "event",
TestData: map[string]any{
"member": map[string]any{
"email": "subscriber@example.com",
"name": "New Subscriber",
"subscribedAt": "2024-01-15T09:00:00Z",
},
"tier": map[string]any{
"name": "Free",
"price": 0,
},
},
},
{
Name: "content.render",
Label: "Content Render",
Description: "Transform rendered HTML",
Pattern: "transform",
TestData: map[string]any{
"html": "<h1>Hello World</h1><p>This is test content.</p><pre><code>const x = 1;</code></pre>",
"post": map[string]any{
"slug": "hello-world",
"title": "Hello World",
"tags": []string{"test"},
},
},
},
{
Name: "asset.uploaded",
Label: "Asset Uploaded",
Description: "Triggered when an asset is uploaded",
Pattern: "event",
TestData: map[string]any{
"id": "asset-123",
"url": "https://cdn.example.com/image.webp",
"contentType": "image/webp",
"size": 102400,
"width": 1920,
"height": 1080,
},
},
{
Name: "analytics.sync",
Label: "Analytics Sync",
Description: "Sync analytics data periodically",
Pattern: "event",
TestData: map[string]any{
"period": map[string]any{
"start": "2024-01-08T00:00:00Z",
"end": "2024-01-15T00:00:00Z",
},
"pageviews": 1250,
"visitors": 890,
"topPages": []map[string]any{
{"path": "/hello-world", "views": 450},
{"path": "/about", "views": 230},
},
},
},
}
// Templates organized by hook and language
var templates = map[string]map[string]string{
"post.published": {
"typescript": `export const onPostPublished = (event: PostPublishedEvent): void => {
Runner.log("Post published: " + event.post.title);
// Example: Send Slack notification
Runner.httpRequest({
url: Runner.secrets.SLACK_WEBHOOK,
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: "New post: " + event.post.title + "\n" + event.post.url,
}),
});
};
`,
"go": `package main
func OnPostPublished(event PostPublishedEvent) error {
Runner.Log("Post published: " + event.Post.Title)
// Example: Send Slack notification
Runner.HttpRequest(Runner.Secrets.SlackWebhook, "POST", []byte("{\"text\":\"New post published\"}"))
return nil
}
func main() {}
`,
},
"post.updated": {
"typescript": `export const onPostUpdated = (event: PostUpdatedEvent): void => {
Runner.log("Post updated: " + event.post.title);
// Example: Sync to external CMS
if (event.changes.content) {
Runner.httpRequest({
url: "https://api.example.com/sync",
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
slug: event.post.slug,
title: event.post.title,
url: event.post.url,
}),
});
}
};
`,
"go": `package main
func OnPostUpdated(event PostUpdatedEvent) error {
Runner.Log("Post updated: " + event.Post.Title)
return nil
}
func main() {}
`,
},
"comment.validate": {
"typescript": `export const validateComment = (input: CommentInput): ValidationResult => {
// Example: Simple spam check
const spamWords = ["buy now", "click here", "free money"];
const content = input.content.toLowerCase();
for (const word of spamWords) {
if (content.includes(word)) {
return { allowed: false, reason: "Comment flagged as spam" };
}
}
// Example: Check minimum length
if (input.content.length < 10) {
return { allowed: false, reason: "Comment too short" };
}
return { allowed: true };
};
`,
"go": `package main
import "strings"
func ValidateComment(input CommentInput) (ValidationResult, error) {
// Example: Simple spam check
spamWords := []string{"buy now", "click here", "free money"}
content := strings.ToLower(input.Content)
for _, word := range spamWords {
if strings.Contains(content, word) {
return ValidationResult{Allowed: false, Reason: "Spam detected"}, nil
}
}
return ValidationResult{Allowed: true}, nil
}
func main() {}
`,
},
"comment.created": {
"typescript": `export const onCommentCreated = (event: CommentCreatedEvent): void => {
Runner.log("New comment on: " + event.post.title);
// Example: Send notification
Runner.httpRequest({
url: Runner.secrets.SLACK_WEBHOOK,
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: "New comment by " + event.comment.authorName + " on \"" + event.post.title + "\"",
}),
});
};
`,
"go": `package main
func OnCommentCreated(event CommentCreatedEvent) error {
Runner.Log("New comment on: " + event.Post.Title)
return nil
}
func main() {}
`,
},
"member.subscribed": {
"typescript": `export const onMemberSubscribed = (event: MemberSubscribedEvent): void => {
Runner.log("New subscriber: " + event.member.email);
// Example: Add to email list
Runner.httpRequest({
url: "https://api.buttondown.email/v1/subscribers",
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Token " + Runner.secrets.BUTTONDOWN_API_KEY,
},
body: JSON.stringify({
email: event.member.email,
notes: "Subscribed from blog",
}),
});
};
`,
"go": `package main
func OnMemberSubscribed(event MemberSubscribedEvent) error {
Runner.Log("New subscriber: " + event.Member.Email)
return nil
}
func main() {}
`,
},
"content.render": {
"typescript": `export const renderContent = (input: ContentRenderInput): ContentRenderOutput => {
let html = input.html;
// Example: Add copy button to code blocks
html = html.replace(
/<pre><code/g,
'<pre class="relative group"><button class="copy-btn absolute top-2 right-2 opacity-0 group-hover:opacity-100">Copy</button><code'
);
// Example: Make external links open in new tab
html = html.replace(
/<a href="(https?:\/\/[^"]+)"/g,
'<a href="$1" target="_blank" rel="noopener"'
);
return { html };
};
`,
"go": `package main
import "strings"
func RenderContent(input ContentRenderInput) (ContentRenderOutput, error) {
html := input.Html
// Example: Add copy button to code blocks
html = strings.ReplaceAll(html, "<pre><code", "<pre class=\"relative\"><button class=\"copy-btn\">Copy</button><code")
return ContentRenderOutput{Html: html}, nil
}
func main() {}
`,
},
"asset.uploaded": {
"typescript": `export const onAssetUploaded = (event: AssetUploadedEvent): void => {
Runner.log("Asset uploaded: " + event.url);
// Example: Backup to external storage
if (event.contentType.startsWith("image/")) {
Runner.httpRequest({
url: "https://api.cloudinary.com/v1_1/demo/image/upload",
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
file: event.url,
folder: "blog-backups",
}),
});
}
};
`,
"go": `package main
func OnAssetUploaded(event AssetUploadedEvent) error {
Runner.Log("Asset uploaded: " + event.Url)
return nil
}
func main() {}
`,
},
"analytics.sync": {
"typescript": `export const onAnalyticsSync = (event: AnalyticsSyncEvent): void => {
Runner.log("Analytics sync: " + event.pageviews + " pageviews");
// Example: Push to external analytics
Runner.httpRequest({
url: "https://api.example.com/analytics",
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + Runner.secrets.ANALYTICS_KEY,
},
body: JSON.stringify({
period: event.period,
pageviews: event.pageviews,
visitors: event.visitors,
topPages: event.topPages,
}),
});
};
`,
"go": `package main
func OnAnalyticsSync(event AnalyticsSyncEvent) error {
Runner.Log("Analytics sync completed")
return nil
}
func main() {}
`,
},
}
// GetTemplate returns the template for a specific hook and language
func GetTemplate(hook, language string) string {
if hookTemplates, ok := templates[hook]; ok {
if template, ok := hookTemplates[language]; ok {
return template
}
}
// Default fallback
return templates["post.published"]["typescript"]
}