Skip to content

Age transformation AI app powered by Next.js, Vercel, Replicate, Upstash, and Cloudflare R2 + Workers.

License

Notifications You must be signed in to change notification settings

steven-tey/extrapolate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

9003193 · Dec 15, 2024
Dec 15, 2024
Dec 15, 2024
Aug 29, 2024
Aug 12, 2024
May 24, 2024
May 16, 2024
May 14, 2024
May 24, 2024
Aug 9, 2024
May 8, 2024
Jan 19, 2023
May 14, 2024
May 23, 2024
May 16, 2024
May 8, 2024
May 8, 2024
Aug 29, 2024
Aug 29, 2024
Jan 19, 2023
May 16, 2024
May 8, 2024
May 20, 2024

Repository files navigation

Extrapolate – See how well you age with AI

See how well you age with AI

Steven Tey Twitter follower count Extrapolate repo star count

Introduction · Features · Deploy Your Own · Author


--NEW README COMING SOON--

Introduction

Extrapolate is an app for you to see how well you age by transforming your face with Artificial Intelligence.

Extrapolate.mp4

Features

  • 3s GIF of your face as it ages through time 🧓
  • Store & retrieve photos from Cloudflare R2 using Workers

Deploy Your Own

You can deploy this template to Vercel with the button below:

Deploy with Vercel

Note that you'll need to:

Cloudflare R2 setup instructions

  1. Go to Cloudflare and create an R2 bucket.
  2. Create a Cloudflare Worker using the code snippet below.
  3. Bind your worker to your R2 instance under Settings > R2 Bucket Bindings.
  4. For extra security, set an AUTH_KEY_SECRET variable under Settings > Environment Variables (you can generate a random secret here).
  5. Replace all instances of images.extrapolate.workers.dev in the codebase with your Cloudflare Worker endpoint.
Cloudflare Worker Code
// Check requests for a pre-shared secret
const hasValidHeader = (request, env) => {
  return request.headers.get("X-CF-Secret") === env.AUTH_KEY_SECRET;
};

function authorizeRequest(request, env, key) {
  switch (request.method) {
    case "PUT":
    case "DELETE":
      return hasValidHeader(request, env);
    case "GET":
      return true;
    default:
      return false;
  }
}

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const key = url.pathname.slice(1);

    if (!authorizeRequest(request, env, key)) {
      return new Response("Forbidden", { status: 403 });
    }

    switch (request.method) {
      case "PUT":
        await env.MY_BUCKET.put(key, request.body);
        return new Response(`Put ${key} successfully!`);
      case "GET":
        const object = await env.MY_BUCKET.get(key);

        if (object === null) {
          return new Response("Object Not Found", { status: 404 });
        }

        const headers = new Headers();
        object.writeHttpMetadata(headers);
        headers.set("etag", object.httpEtag);

        return new Response(object.body, {
          headers,
        });
      case "DELETE":
        await env.MY_BUCKET.delete(key);
        return new Response("Deleted!");

      default:
        return new Response("Method Not Allowed", {
          status: 405,
          headers: {
            Allow: "PUT, GET, DELETE",
          },
        });
    }
  },
};

Author