1+ from workers import WorkerEntrypoint , Response , Request , fetch
2+ from js import HTMLRewriter
3+ from urllib .parse import urlparse
4+ from html import escape
5+
6+ from pyodide .ffi import to_js
7+
8+ class MetaTagInjector :
9+ """
10+ Element handler for HTMLRewriter that injects OpenGraph meta tags.
11+ Uses Python's html.escape() for proper HTML escaping.
12+ """
13+ def __init__ (self , og_data : dict ):
14+ self .og_data = og_data
15+ self .injected = False
16+
17+ def element (self , element ):
18+ """Called when the <head> element is encountered."""
19+ if not self .injected :
20+ # Create and inject meta tags
21+ self ._inject_meta_tags (element )
22+ self .injected = True
23+
24+ def _inject_meta_tags (self , head_element ):
25+ """Inject OpenGraph and Twitter Card meta tags."""
26+ # OpenGraph tags
27+ self ._create_meta (head_element , "property" , "og:title" , self .og_data ["title" ])
28+ self ._create_meta (head_element , "property" , "og:description" , self .og_data ["description" ])
29+ self ._create_meta (head_element , "property" , "og:image" , self .og_data ["image" ])
30+ self ._create_meta (head_element , "property" , "og:url" , self .og_data ["url" ])
31+ self ._create_meta (head_element , "property" , "og:type" , self .og_data ["type" ])
32+ self ._create_meta (head_element , "property" , "og:site_name" , self .og_data ["site_name" ])
33+
34+ # Twitter Card tags
35+ self ._create_meta (head_element , "name" , "twitter:card" , "summary_large_image" )
36+ self ._create_meta (head_element , "name" , "twitter:title" , self .og_data ["title" ])
37+ self ._create_meta (head_element , "name" , "twitter:description" , self .og_data ["description" ])
38+ self ._create_meta (head_element , "name" , "twitter:image" , self .og_data ["image" ])
39+
40+ def _create_meta (self , head_element , attr_name : str , attr_value : str , content : str ):
41+ """
42+ Create a meta tag and prepend it to the head element.
43+ Uses Python's html.escape() for proper attribute escaping.
44+ """
45+ # Use Python's built-in html.escape() which handles all necessary escaping
46+ escaped_attr_value = escape (attr_value , quote = True )
47+ escaped_content = escape (content , quote = True )
48+ meta_html = f'<meta { attr_name } ="{ escaped_attr_value } " content="{ escaped_content } " />'
49+ head_element .prepend (meta_html , html = True )
50+
51+
52+ class ExistingMetaRemover :
53+ """
54+ Element handler that removes existing OpenGraph and Twitter meta tags.
55+ """
56+ def element (self , element ):
57+ """Remove the element by calling remove()."""
58+ element .remove ()
59+
60+
61+ class Default (WorkerEntrypoint ):
62+ """
63+ OpenGraph Meta Tag Injection Example
64+
65+ This Worker fetches a web page and injects OpenGraph meta tags
66+ based on the request path using Cloudflare's HTMLRewriter API.
67+ """
68+
69+ async def fetch (self , request : Request ):
70+ # Parse the request path to determine which page we're serving
71+ url = urlparse (request .url )
72+ path = url .path
73+
74+ # Define OpenGraph metadata based on the path
75+ og_data = self .get_opengraph_data (path )
76+
77+ # Fetch the original HTML from a target website
78+ # In this example, we'll use example.com, but you can replace this
79+ # with your actual website URL
80+ #
81+ # Note that this isn't necessary if your worker will also be serving
82+ # content of your website, in that case you should already have the HTML
83+ # you're returning ready to go here.
84+ target_url = f"https://example.com{ path } "
85+
86+ # Fetch the original page
87+ response = await fetch (target_url )
88+
89+ # Use HTMLRewriter to inject OpenGraph meta tags
90+ rewritten_response = self .inject_opengraph_tags (response , og_data )
91+
92+ return rewritten_response
93+
94+ def get_opengraph_data (self , path : str ) -> dict :
95+ """
96+ Generate OpenGraph metadata based on the request path.
97+ Customize this function to match your site's structure.
98+ """
99+ # Default metadata
100+ og_data = {
101+ "title" : "My Awesome Website" ,
102+ "description" : "Welcome to my website built with Python Workers!" ,
103+ "image" : "https://images.unsplash.com/photo-1518770660439-4636190af475" ,
104+ "url" : f"https://yoursite.com{ path } " ,
105+ "type" : "website" ,
106+ "site_name" : "Python Workers Demo"
107+ }
108+
109+ # Customize based on path
110+ if path .startswith ("/blog/" ):
111+ article_slug = path .replace ("/blog/" , "" ).strip ("/" )
112+ og_data .update ({
113+ "title" : f"Blog Post: { article_slug .replace ('-' , ' ' ).title ()} " ,
114+ "description" : f"Read our latest article about { article_slug .replace ('-' , ' ' )} " ,
115+ "image" : "https://images.unsplash.com/photo-1499750310107-5fef28a66643" ,
116+ "type" : "article"
117+ })
118+ elif path .startswith ("/products/" ):
119+ product_slug = path .replace ("/products/" , "" ).strip ("/" )
120+ og_data .update ({
121+ "title" : f"Product: { product_slug .replace ('-' , ' ' ).title ()} " ,
122+ "description" : f"Check out our amazing { product_slug .replace ('-' , ' ' )} product" ,
123+ "image" : "https://images.unsplash.com/photo-1505740420928-5e560c06d30e" ,
124+ "type" : "product"
125+ })
126+ elif path == "/about" :
127+ og_data .update ({
128+ "title" : "About Us - Python Workers" ,
129+ "description" : "Learn more about our team and what we do with Python Workers" ,
130+ "image" : "https://images.unsplash.com/photo-1522071820081-009f0129c71c"
131+ })
132+
133+ return og_data
134+
135+ def inject_opengraph_tags (self , response , og_data : dict ):
136+ """
137+ Use HTMLRewriter to inject OpenGraph meta tags into the HTML response.
138+ Removes existing OG tags first to avoid duplicates.
139+ """
140+ # Create an HTMLRewriter instance
141+ rewriter = HTMLRewriter .new ()
142+
143+ # Remove existing OpenGraph and Twitter meta tags to avoid duplicates
144+ meta_remover = ExistingMetaRemover ()
145+ rewriter .on ('meta[property^="og:"]' , to_js (meta_remover ))
146+ rewriter .on ('meta[name^="twitter:"]' , to_js (meta_remover ))
147+
148+ # Inject new OpenGraph meta tags into the <head> element
149+ meta_injector = MetaTagInjector (og_data )
150+ rewriter .on ("head" , to_js (meta_injector ))
151+
152+ # Transform the response
153+ return rewriter .transform (response .js_object )
0 commit comments