chore: updated Edge Functions examples (#18742)

* Removed the use of deprecated std/http in favor of Deno.serve
* Use full import paths instead of using paths from import maps (less
  confusing, easy to copy examples)
* Formatted all examples with `deno fmt`
This commit is contained in:
Lakshan Perera authored and GitHub committed 2023-11-06 19:55:22 +11:00
1 parent 3ef54467d9
commit 67367d7f4e
30 files changed
+125 -131

No files matched your search

@@ -2,12 +2,11 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { corsHeaders } from '../_shared/cors.ts'
console.log(`Function "browser-with-cors" up and running!`)
serve(async (req) => {
Deno.serve(async (req) => {
// This is needed if you're planning to invoke your function from a browser.
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
@@ -2,7 +2,6 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { corsHeaders } from '../_shared/cors.ts'
console.log(`Function "cloudflare-turnstile" up and running!`)
@@ -11,7 +10,7 @@ function ips(req: Request) {
return req.headers.get('x-forwarded-for')?.split(/\s*,\s*/)
}
serve(async (req) => {
Deno.serve(async (req) => {
// This is needed if you're planning to invoke your function from a browser.
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
@@ -1,5 +1,5 @@
import { Application, Router } from 'https://deno.land/x/oak@v11.1.0/mod.ts'
import { Session, CookieStore } from 'https://deno.land/x/oak_sessions@v4.1.9/mod.ts'
import { CookieStore, Session } from 'https://deno.land/x/oak_sessions@v4.1.9/mod.ts'
import { OAuth2Client } from 'https://deno.land/x/oauth2_client@v1.0.2/mod.ts'
import { SupabaseManagementAPI } from 'https://esm.sh/supabase-management-js@0.1.2'
@@ -5,10 +5,10 @@
// Sift is a small routing library that abstracts away details like starting a
// listener on a port, and provides a simple function (serve) that has an API
// to invoke a function for a specific path.
import { json, serve, validateRequest } from 'sift'
import { json, serve, validateRequest } from 'https://deno.land/x/sift@0.6.0/mod.ts'
// TweetNaCl is a cryptography library that we use to verify requests
// from Discord.
import nacl from 'nacl'
import nacl from 'https://cdn.skypack.dev/tweetnacl@v1.0.3?dts'
enum DiscordCommandType {
Ping = 1,
@@ -1,8 +1,8 @@
// This example shows how to use Edge Functions to read incoming multipart/form-data request,
// and write files to Supabase Storage and other fields to a database table.
import { Application } from 'oak'
import { createClient } from '@supabase/supabase-js'
import { Application } from 'https://deno.land/x/oak@v11.1.0/mod.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
const MB = 1024 * 1024
@@ -49,9 +49,10 @@ app.use(async (ctx) => {
}
// insert record to messages table
const { error } = await supabaseClient
.from('comments')
.insert({ message: formData.fields!.message || '', image_path: upload.path })
const { error } = await supabaseClient.from('comments').insert({
message: formData.fields!.message || '',
image_path: upload.path,
})
if (error) {
console.error(error)
ctx.response.status = 500
@@ -2,8 +2,7 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { createClient } from '@supabase/supabase-js'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
import { corsHeaders } from '../_shared/cors.ts'
console.log(`Function "get-tshirt-competition" up and running!`)
@@ -23,7 +22,7 @@ function turnEmailToCount(email: string): string {
)}`
}
serve(async (req) => {
Deno.serve(async (req) => {
// This is needed if you're planning to invoke your function from a browser.
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
@@ -41,15 +40,17 @@ serve(async (req) => {
!answer ||
!twitter ||
!size
)
) {
throw new Error(
`Please provide valid 'email', 'twitter', 'size', and 'answer' params. HINT: https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts`
)
}
if (answer !== countEmailSegments(email!))
if (answer !== countEmailSegments(email!)) {
throw new Error(
`Sorry, that's wrong, please try again! HINT: https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts`
)
}
const supabaseAdminClient = createClient(
// Supabase API URL - env var exported by default when deployed.
@@ -2,11 +2,9 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
console.log('Hello from Functions!')
serve((_req) => {
Deno.serve((_req) => {
const data = {
message: `I was deployed via GitHub Actions!`,
}
@@ -1,6 +1,5 @@
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { HfInference } from 'https://esm.sh/@huggingface/inference@2.3.2'
import { createClient } from '@supabase/supabase-js'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
import { Database } from './types.ts'
console.log('Hello from `huggingface-image-captioning` function!')
@@ -16,7 +15,7 @@ interface WebhookPayload {
old_record: null | SoRecord
}
serve(async (req) => {
Deno.serve(async (req) => {
const payload: WebhookPayload = await req.json()
const soRecord = payload.record
const supabaseAdminClient = createClient<Database>(
@@ -2,11 +2,10 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { Pool } from 'postgres'
import {
Kysely,
Generated,
Kysely,
PostgresAdapter,
PostgresIntrospector,
PostgresQueryCompiler,
@@ -59,7 +58,7 @@ const db = new Kysely<Database>({
},
})
serve(async (_req) => {
Deno.serve(async (_req) => {
try {
// Run a query
const animals = await db.selectFrom('animals').select(['id', 'animal', 'created_at']).execute()
@@ -2,13 +2,11 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
function ips(req: Request) {
return req.headers.get('x-forwarded-for')?.split(/\s*,\s*/)
}
serve(async (req) => {
Deno.serve(async (req) => {
const clientIps = ips(req) || ['']
const res = await fetch(
`https://ipinfo.io/${clientIps[0]}?token=${Deno.env.get('IPINFO_TOKEN')}`,
@@ -1,4 +1,4 @@
import { Application, Router } from 'oak'
import { Application, Router } from 'https://deno.land/x/oak@v11.1.0/mod.ts'
const router = new Router()
router
@@ -1,6 +1,6 @@
import React from 'React'
import { ImageResponse } from 'og_edge'
import { createClient } from '@supabase/supabase-js'
import React from 'https://esm.sh/react@18.2.0?deno-std=0.177.0'
import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
import { corsHeaders } from '../_shared/cors.ts'
const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw6'
@@ -20,11 +20,12 @@ export async function handler(req: Request) {
const name = url.searchParams.get('name') ?? url.searchParams.get('amp;name')
const golden = url.searchParams.get('golden') ?? url.searchParams.get('amp;golden')
if (!username || !ticketNumber || !name)
if (!username || !ticketNumber || !name) {
return new Response(JSON.stringify({ error: 'missing params' }), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 400,
})
}
try {
// Try to get image from Supabase Storage CDN.
@@ -2,10 +2,8 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { handler } from './handler.tsx'
console.log(`Function "og-image-with-storage-cdn" up and running!`)
serve(handler)
Deno.serve(handler)
@@ -1,8 +1,7 @@
import 'xhr_polyfill'
import { serve } from 'std/server'
import { CreateCompletionRequest } from 'openai'
import 'https://deno.land/x/xhr@0.3.0/mod.ts'
import { CreateCompletionRequest } from 'https://esm.sh/openai@3.1.0'
serve(async (req) => {
Deno.serve(async (req) => {
const { query } = await req.json()
const completionConfig: CreateCompletionRequest = {
@@ -1,5 +1,5 @@
import React from 'React'
import { ImageResponse } from 'og_edge'
import React from 'https://esm.sh/react@18.2.0?deno-std=0.177.0'
import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts'
export function handler(req: Request) {
return new ImageResponse(
@@ -2,10 +2,8 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { handler } from './handler.tsx'
console.log(`Function "opengraph" up and running!`)
serve(handler)
Deno.serve(handler)
@@ -1,5 +1,4 @@
import { Pool } from 'postgres'
import { serve } from 'std/server'
import { Pool } from 'https://deno.land/x/postgres@v0.17.0/mod.ts'
// Create a database pool with one connection.
const pool = new Pool(
@@ -14,7 +13,7 @@ const pool = new Pool(
1
)
serve(async (_req) => {
Deno.serve(async (_req) => {
try {
// Grab a connection from the pool
const connection = await pool.connect()
@@ -1,7 +1,6 @@
import { serve } from 'std/server'
import puppeteer from 'puppeteer'
import puppeteer from 'https://deno.land/x/puppeteer@16.2.0/mod.ts'
serve(async (req) => {
Deno.serve(async (req) => {
try {
console.log(`wss://chrome.browserless.io?token=${Deno.env.get('PUPPETEER_BROWSERLESS_IO_KEY')}`)
// Visit browserless.io to get your free API token
@@ -17,7 +16,9 @@ serve(async (req) => {
await page.goto(url)
const screenshot = await page.screenshot()
return new Response(screenshot, { headers: { 'Content-Type': 'image/png' } })
return new Response(screenshot, {
headers: { 'Content-Type': 'image/png' },
})
} catch (e) {
console.error(e)
return new Response(JSON.stringify({ error: e.message }), {
@@ -2,15 +2,14 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { createClient } from '@supabase/supabase-js'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey',
}
serve(async (req) => {
Deno.serve(async (req) => {
// read a text file from storage and print its contents
// This is needed if you're planning to invoke your function from a browser.
if (req.method === 'OPTIONS') {
@@ -26,7 +25,11 @@ serve(async (req) => {
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
// Create client with Auth context of the user that called the function.
// This way your row-level-security (RLS) policies are applied.
{ global: { headers: { Authorization: req.headers.get('Authorization')! } } }
{
global: {
headers: { Authorization: req.headers.get('Authorization')! },
},
}
)
const { data, error } = await supabaseClient.storage.from('my-bucket').download('sample.txt')
@@ -2,8 +2,7 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { createClient, SupabaseClient } from '@supabase/supabase-js'
import { createClient, SupabaseClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
@@ -65,7 +64,7 @@ async function createTask(supabaseClient: SupabaseClient, task: Task) {
})
}
serve(async (req) => {
Deno.serve(async (req) => {
const { url, method } = req
// This is needed if you're planning to invoke your function from a browser.
@@ -82,7 +81,11 @@ serve(async (req) => {
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
// Create client with Auth context of the user that called the function.
// This way your row-level-security (RLS) policies are applied.
{ global: { headers: { Authorization: req.headers.get('Authorization')! } } }
{
global: {
headers: { Authorization: req.headers.get('Authorization')! },
},
}
)
// For more details on URLPattern, check https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
@@ -2,13 +2,12 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { createClient } from '@supabase/supabase-js'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
import { corsHeaders } from '../_shared/cors.ts'
console.log(`Function "select-from-table-with-auth-rls" up and running!`)
serve(async (req: Request) => {
Deno.serve(async (req: Request) => {
// This is needed if you're planning to invoke your function from a browser.
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
@@ -23,7 +22,11 @@ serve(async (req: Request) => {
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
// Create client with Auth context of the user that called the function.
// This way your row-level-security (RLS) policies are applied.
{ global: { headers: { Authorization: req.headers.get('Authorization')! } } }
{
global: {
headers: { Authorization: req.headers.get('Authorization')! },
},
}
)
// Now we can get the session or user object
const {
@@ -1,5 +1,3 @@
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
const RESEND_API_KEY = Deno.env.get('RESEND_API_KEY')
const handler = async (_request: Request): Promise<Response> => {
@@ -27,4 +25,4 @@ const handler = async (_request: Request): Promise<Response> => {
})
}
serve(handler)
Deno.serve(handler)
@@ -2,14 +2,13 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { SmtpClient } from 'denomailer'
import { SmtpClient } from 'https://deno.land/x/denomailer@0.12.0/mod.ts'
const smtp = new SmtpClient()
console.log(`Function "send-email-smtp" up and running!`)
serve(async (_req) => {
Deno.serve(async (_req) => {
await smtp.connect({
hostname: Deno.env.get('SMTP_HOSTNAME')!,
port: Number(Deno.env.get('SMTP_PORT')!),
@@ -1,33 +1,32 @@
import { serve } from 'https://deno.land/std@0.197.0/http/server.ts';
import { WebClient } from 'https://deno.land/x/slack_web_api@6.7.2/mod.js';
import { WebClient } from 'https://deno.land/x/slack_web_api@6.7.2/mod.js'
const slackBotToken = Deno.env.get("SLACK_TOKEN") ?? "";
const botClient = new WebClient(slackBotToken);
const slackBotToken = Deno.env.get('SLACK_TOKEN') ?? ''
const botClient = new WebClient(slackBotToken)
console.log(`Slack URL verification function up and running!`);
serve(async (req) => {
console.log(`Slack URL verification function up and running!`)
Deno.serve(async (req) => {
try {
const { token, challenge, type, event } = await req.json();
const { token, challenge, type, event } = await req.json()
if (type == 'url_verification') {
return new Response(JSON.stringify({ challenge }), {
headers: { 'Content-Type': 'application/json' },
status: 200,
});
})
} else if (event.type == 'app_mention') {
const { user, text, channel, ts } = event;
const { user, text, channel, ts } = event
// Here you should process the text received and return a response:
const response = await botClient.chat.postMessage({
channel: channel,
text: `Hello <@${user}>!`,
thread_ts: ts,
});
return new Response('ok', { status: 200 });
})
return new Response('ok', { status: 200 })
}
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
headers: { 'Content-Type': 'application/json' },
status: 500,
});
})
}
});
})
@@ -1,8 +1,6 @@
import { serve } from 'std/server'
const msg = new TextEncoder().encode('data: hello\r\n\r\n')
serve((_) => {
Deno.serve((_) => {
let timerId: number | undefined
const body = new ReadableStream({
@@ -2,10 +2,8 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
// Import via bare specifier thanks to the import_map.json file.
import Stripe from 'stripe'
import Stripe from 'https://esm.sh/stripe@11.1.0?target=deno'
const stripe = new Stripe(Deno.env.get('STRIPE_API_KEY') as string, {
// This is needed to use the Fetch API rather than relying on the Node http
@@ -18,7 +16,7 @@ const cryptoProvider = Stripe.createSubtleCryptoProvider()
console.log('Hello from Stripe Webhook!')
serve(async (request) => {
Deno.serve(async (request) => {
const signature = request.headers.get('Stripe-Signature')
// First step is to verify the event. The .text() method must be used as the
@@ -2,11 +2,9 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
console.log(`Function "telegram-bot" up and running!`)
import { Bot, webhookCallback } from 'grammy'
import { Bot, webhookCallback } from 'https://deno.land/x/grammy@v1.8.3/mod.ts'
const bot = new Bot(Deno.env.get('TELEGRAM_BOT_TOKEN') || '')
@@ -16,11 +14,12 @@ bot.command('ping', (ctx) => ctx.reply(`Pong! ${new Date()} ${Date.now()}`))
const handleUpdate = webhookCallback(bot, 'std/http')
serve(async (req) => {
Deno.serve(async (req) => {
try {
const url = new URL(req.url)
if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET'))
if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET')) {
return new Response('not allowed', { status: 405 })
}
return await handleUpdate(req)
} catch (err) {
@@ -2,10 +2,8 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'https://deno.land/std@0.140.0/http/server.ts'
import { handler } from './handler.tsx'
console.log(`Function "tweet-to-image" up and running!`)
serve(handler)
Deno.serve(handler)
@@ -2,12 +2,11 @@
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'std/server'
import { Redis } from 'upstash_redis'
import { Redis } from 'https://deno.land/x/upstash_redis@v1.19.3/mod.ts'
console.log(`Function "upstash-redis-counter" up and running!`)
serve(async (_req) => {
Deno.serve(async (_req) => {
try {
const redis = new Redis({
url: Deno.env.get('UPSTASH_REDIS_REST_URL')!,
@@ -27,14 +26,22 @@ serve(async (_req) => {
const counterHash: Record<string, number> | null = await redis.hgetall('supa-edge-counter')
const counters = Object.entries(counterHash!)
.sort(([, a], [, b]) => b - a) // sort desc
.reduce((r, [k, v]) => ({ total: r.total + v, regions: { ...r.regions, [k]: v } }), {
total: 0,
regions: {},
})
.reduce(
(r, [k, v]) => ({
total: r.total + v,
regions: { ...r.regions, [k]: v },
}),
{
total: 0,
regions: {},
}
)
return new Response(JSON.stringify({ counters }), { status: 200 })
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), { status: 200 })
return new Response(JSON.stringify({ error: error.message }), {
status: 200,
})
}
})
@@ -1,61 +1,60 @@
import { serve } from "std/server";
import { Redis } from "@upstash/redis";
import { Ratelimit } from "@upstash/ratelimit";
import { createClient } from "@supabase/supabase-js";
import { Redis } from 'https://deno.land/x/upstash_redis@v1.19.3/mod.ts'
import { Ratelimit } from 'https://cdn.skypack.dev/@upstash/ratelimit@0.4.4'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
console.log(`Function "upstash-redis-counter" up and running!`);
console.log(`Function "upstash-redis-counter" up and running!`)
serve(async (req) => {
Deno.serve(async (req) => {
try {
// Create a Supabase client with the Auth context of the logged in user.
const supabaseClient = createClient(
// Supabase API URL - env var exported by default.
Deno.env.get("SUPABASE_URL") ?? "",
Deno.env.get('SUPABASE_URL') ?? '',
// Supabase API ANON KEY - env var exported by default.
Deno.env.get("SUPABASE_ANON_KEY") ?? "",
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
// Create client with Auth context of the user that called the function.
// This way your row-level-security (RLS) policies are applied.
{
global: {
headers: { Authorization: req.headers.get("Authorization")! },
headers: { Authorization: req.headers.get('Authorization')! },
},
}
);
)
// Now we can get the session or user object
const {
data: { user },
} = await supabaseClient.auth.getUser();
if (!user) throw new Error("no user");
console.log(user.id);
} = await supabaseClient.auth.getUser()
if (!user) throw new Error('no user')
console.log(user.id)
const redis = new Redis({
url: Deno.env.get("UPSTASH_REDIS_REST_URL")!,
token: Deno.env.get("UPSTASH_REDIS_REST_TOKEN")!,
});
url: Deno.env.get('UPSTASH_REDIS_REST_URL')!,
token: Deno.env.get('UPSTASH_REDIS_REST_TOKEN')!,
})
// Create a new ratelimiter, that allows 10 requests per 10 seconds
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(2, "10 s"),
limiter: Ratelimit.slidingWindow(2, '10 s'),
analytics: true,
});
})
// Use a constant string to limit all requests with a single ratelimit
// Or use a userID, apiKey or ip address for individual limits.
const identifier = user.id;
const { success } = await ratelimit.limit(identifier);
const identifier = user.id
const { success } = await ratelimit.limit(identifier)
if (!success) {
throw new Error("limit exceeded");
throw new Error('limit exceeded')
}
return new Response(JSON.stringify({ success }), { status: 200 });
return new Response(JSON.stringify({ success }), { status: 200 })
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 200,
});
})
}
});
})
// curl -i --location --request POST 'http://localhost:54321/functions/v1/upstash-redis-ratelimit' \
// --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjc4MDA4Mjk2LCJzdWIiOiJkZjQ3ZDU5My0zMjY1LTQ2OWEtOWI5OS1mZDk1OTdhOGU4YzciLCJlbWFpbCI6InRlc3RyQHRlc3Quc2ciLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIiwicHJvdmlkZXJzIjpbImVtYWlsIl19LCJ1c2VyX21ldGFkYXRhIjp7fSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTY3ODAwNDY5Nn1dLCJzZXNzaW9uX2lkIjoiMDNjMmFlYjUtMjk5MC00ZWU0LWIxYTYtZmU1Y2IwMGFhMjU3In0.WwqggrX34j0NINiulC9rsj-cd6HzV55ySxJkJlVsU4o' \