AWS’ten Firebase’e Geçiş: Flask, Firebase ve Google Cloud ile Gelişmiş Bir Web Uygulaması Oluşturma

Mobil ve web geliştirmesi, son yıllarda büyük bir evrim geçirdi. Modern uygulamalar, yalnızca güçlü özelliklere sahip olmakla kalmıyor, aynı zamanda geleneksel barındırma hizmetlerinin ötesinde daha kapsamlı bir altyapıya ihtiyaç duyuyor. Bu noktada, dev şirketler like Amazon ve Google, tüm bu gereksinimleri karşılamak adına kapsamlı çözümler sunuyorlar. AWS ve Firebase gibi platformlar arasında geçiş yaparken, her adımın dikkatle planlanması ve bu karmaşık entegrasyon sürecinin doğru bir şekilde yönetilmesi, projelerin başarılı bir şekilde sunucusuz mimarilere uyum sağlaması gerekmektedir. Bu yazıda, temel aldığım AWS akışını Firebase servisleri kullanarak Firebase platformuna aktarma sürecini anlatacağım. Bu konuyu açıklarken, Firebase ve AWS platformlarının aslında birbirine ne kadar benzer yapılar sunduğunu keşfedeceğiz. Ayrıca Firebase Bulut Mesajlaşma(Firebase Cloud Messaging) servisi ile de kullanıcıya nasıl bildirim göndereceğimizi inceleyeceğiz.

AWS Akışının Tanımı

Şekil 1 Kullanıcıdan Girdi Alıp Replicate’e Gönderildiği ve Sonucun Tekrar Kullanıcıya Döndürüldüğü AWS Akışı

Şekil 1’deki AWS akışında kullanıcıdan bir girdi bilgisi ve bağlantı id bilgisi alınmaktadır. Alınan bu bilgiler AWS Lambda fonksiyonu ile makine öğrenmesi modellerini bulut ortamında çalıştıran Replicate servisine gitmektedir. Replicate kullanılacak olan makine öğrenmesi modellerini Uygulama Programlama Arabirimi(Application Programming Interface) veya Docker formatında kullanıcılara sunmaktadır. Biz bu yazımızda kullanacağımız modelleri UPA formatında kullanacağız. Replicate UPA’larında web kancası servisi(Webhook) yapısını kullanabilmekteyiz. Web kancası servisi yardımı ile Replicate UPA’sı üzerinden gelen dönüş farklı bir URL’e yönlendirilmektedir. Web kancası servisi ile gelen dönüş AWS Lambda ile AWS S3 bucket içerisine kaydedilmektedir. Kaydedilen dönüş Websocket yardımı ile kullanıcıya döndürülmektedir.

Yukarıda açıklanan Şekil 1’deki AWS akışı aşağıdaki adımlara firebase yapısına çevrilmiştir.

  1. Flask Uygulaması Kurulumu:
    1. Temel bir Flask projesi oluşturarak başlanır.
  2. Firebase Bulut Mesajlaşma Entegrasyonu:
    1. Flask projesinde FBM jeton(token) bilgilerin Firebase Bulut Mesajlaşma konfigürasyonları ile elde edilir.
  3. AWS Replicate Fonksiyonu Dönüşümü:
    1. AWS Replicate’e Yolla Lambda fonksiyonunu Firebase Bulut Fonksiyonları ile bulut fonksiyonu haline getirilir.
    2. Fonksiyon içerisinde Replicate’de kullanılan UPA’nın konfigürasyonları yapılmaktadır, bunlara ek olarak FBM jeton bilgisi ve web kancası servisi URL’i de UPA üzerinden gönderilmektedir.
  4. Web Kancası Servisi URL İşleme:
    1. Replicate’ e gönderilen Web kancası servisi URL’i Şekil 1’deki AWS S3’ye kaydet lambda fonksiyonun karşılığı olan, UPA’dan gelen resimi Google Bulut Depolama’ya kaydeden fonksiyona gönderilmektedir.
  5. AWS S3’ye Kaydet Fonksiyonu Dönüşümü:
    1. Firebase Bulut Fonksiyonu içinde:
      1. Replicate’ten alınan resim Google Bulut Depolama’da kaydedilir.
      2. Resim URL’i ve FBM jeton bilgisi çıkarılır.
      3. Uygulama içinde bildirim gönderen Firebase Fonksiyona POST request ile resim URL’i ve FBM jeton bilgileri gönderilmektedir.
  6. Firebase Bildirim Gönderme Fonksiyonu:
    1. Bu fonksiyon, FBM aracılığıyla bir bildirim göndermeyi sağlar. İsteğin JSON verisi içindeki image_url ve jeton bilgilerini alır, ardından bu bilgileri kullanarak belirtilen cihaza bir bildirim gönderir. 

Flask Uygulaması

Kullanıcıya bildirim gönderebilmek için kullanıcı tarafında bir yapı olması gerektiği için basit bir Flask uygulaması oluşturuldu. Flask uygulaması içinde, kullanıcıdan gelen girdi bilgisini bir Replicate UPA’sına iletmek için, Şekil 2’deki gibi kullanıcıdan girdi alınmaktadır. Metin olarak alınan girdi bilgisi ve sayfa ilk açıldığında oluşturulan FBM jeton bilgisi Flask sunucu tarafında AWS Replicate’e Yolla Lambda fonksiyonunun karşılığı olan fonksiyona gönderilmektedir. 

Şekil 2 Kullanıcıdan Girdi Alınan ve FBM Jeton Bilgisinin Konsolda Gösterildiği Flask Uygulaması Ekranı

Firebase Bulut Mesajlaşma Entegrasyonu

Firebase Yazılım Geliştirme Seti(Software Development Kit) Oluşturma

Öncelikle Firebase konsoluna giriş yapıyoruz ardından Şekil 3’deki gösterilen şekilde yeni bir Firebase projesi oluşturuyoruz.

Şekil 3 Firebase Konsolu Yeni Proje Oluşturma/Var Olan Projelerin Listesi Ekranı

Oluşturduğumuz projenin içerisinde yer alan proje ayarları bölümüne geliyoruz. Proje ayarları bölümünde servis hesapları sekmesinde yeni özel anahtar oluştur diyoruz. Yönetici YGK yapılandırma bölümünde projede kullandığımız dil yapısına uygun olan şekilde indirdiğimiz özel anahtar dosyasını nasıl kullanacağımız gösterilmektedir. 

Örnek bir özel anahtar dosyasının içerisindekiler aşağıdaki gibidir.

{
  "type": "service_account",
  "project_id": "your_project_id",
  "private_key_id": "your_private_key_id",
  "private_key": "your_private_key",
  "client_email": "your_client_email.com",
  "client_id": "your_client_id",
  "auth_uri": "your_auth_uri",
  "token_uri": "your_token_uri",
  "auth_provider_x509_cert_url": "auth_provider_x509_cert_url",
  "client_x509_cert_url": "client_x509_cert_url",
  "universe_domain": "universe_domain"
}

Aynı ayarlar sayfasında General sekmesinde uygulamanız içerisine firebase YGK’sını nasıl yükleyeceğiniz Şekil 4’deki gibi gösterilmektedir. YGK bağlantısı için Şekil 4’de gösterilen bilgiler ile firebase-messaging-sw.js konfigürasyon dosyasını oluşturmamız gerekmektedir. Bu dosya projenin kök klasör lokasyonunda bulunmalıdır.

Şekil 4 Firebase YGK Konfigürasyonlarının Uygulamaya Eklenmesi Entegrasyonu

YGK Dosyasının Flask Uygulamasına Ekleme

Flask uygulamamız içerisinde FBM jeton bilgisini alabilmek için bir Javascript fonksiyonu yazmamız gerekmektedir. Aşağıda bulunan “Firebase FBM Jeton Oluşturma” kodu, FBM kullanarak tarayıcıda anlık bildirimleri almak için gerekli işlemleri gerçekleştirmektedir. İlk olarak, firebaseConfig ile Firebase’i başlatmakta ve ardından messaging örneği oluşturarak bildirimlerle etkileşim kurmayı sağlamaktadır. registerServiceWorker fonksiyonu, tarayıcıda bir Servis işçisi(Service worker) kaydeder. Servis işçisi, genellikle çevrimdışı erişim ve bildirimler gibi özellikleri etkinleştirmek için kullanılır. Servis işçisi başarıyla kaydedildiğinde, konsola kapsam bilgisini yazdırır; hata durumunda ise bir hata mesajı konsola yazdırılır. Bu kod, kullanıcıya anlık bildirimleri gönderme yeteneğini etkinleştirirken, olası hata durumlarını ele alarak güvenli bir şekilde işlem yapmayı sağlar. Öncelikle firebase ile servis işçisi oluşturuyoruz ve firebase bulut mesajlaşma servisine cihazımızı kaydediyoruz. Firebase Bulut Mesajlaşma servisi bize cihaza özgü eşsiz bir jeton bilgisi dönmektedir. Bu jeton sayesinde cihaza bildirim göndermemiz mümkün olmaktadır. Ardından bu FBM jeton bilgisini sunucu tarafında python fonksiyonuna gönderiyoruz. Python fonksiyonu ise bu jeton bilgisini ve kullanıcıdan girdi olarak gelen bilgi firebase bulut fonksiyonuna göndermektedir.

firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();


function registerServiceWorker() {
  return navigator.serviceWorker.register('/firebase-messaging-sw.js')
    .then((registration) => {
      console.log('Service Worker registered with scope:', registration.scope);
      return registration;
    })
    .catch((error) => {
      console.error('Service Worker registration failed:', error);
      throw error;
    });
}

function requestAndSendFCMToken() {
  return messaging.requestPermission()
    .then(async () => {
      const token = await messaging.getToken();
      console.log('FCM token:', token);
      return token;
    })
    .catch((error) => {
      console.error('Error requesting permission or getting FCM token:', error);
      throw error;
    });
}

AWS Replicate Fonksiyonu Dönüşümü

Google Bulut Fonksiyonları ile Bulut Fonksiyonu Oluşturma

Google Bulut konsoluna giriş yapıyoruz. Giriş yaptıktan sonra sol üst kısımdan Firebase üzerinden oluşturduğumuz projemizi seçiyoruz ve arama çubuğuna Google Bulut Fonksiyonları yazarak servise gidiyoruz.

Bulut Fonksiyonları servisinde yeni bir fonksiyon oluşturmak için fonksiyon oluştur(create function) butonuna tıklıyoruz. Şekil 5 gösterilen bölümleri fonksiyonunuzun yapısına göre düzenliyoruz.

Şekil 5 Bulut Fonksiyonları Fonksiyon Tanımının Yapılması ve Erişim Bilgilerinin Ayarlanması

Şekil 6’da gösterildiği gibi Google Bulut Fonksiyonları birden fazla dilde yazılabilmektedir. Başlangıç noktası olarak isimlendiren bölümde yazdığımız fonksiyonun içerisindeki ana çalışacak olan fonksiyonun ismini vermekteyiz.

Şekil 6 Bulut Fonksiyonları Kullanılacak Yazılım Dilinin ve Versiyonunun Seçilmesi

Google Bulut Fonksiyonları ile Replicate UPA’sına Sorgu Gönderme Fonksiyonu

Bu projede oluşturulan tüm fonksiyonlar Python dilinde yazılmış olup Python 3.10 versiyonu kullanılmıştır. Fonksiyon için kullanacağımız dili seçtikten sonra python diline özel bir requirements.txt dosyası oluşturuyoruz. Fonksiyonumuzun bulunacağı .py dosyasına  aşağıdaki örnekteki gibi bir Replicate sorgusu yazıyoruz. “Firebase Bulut Fonksiyonu – Replicate’e Sorgu Gönderme” fonksiyonunda , Flask tarafından gelen jeton bilgisi ve kullanıcıdan girdi bilgisi Replicate sitesinde bulunan stable diffusion modeline sorgu atılmaktadır. Sorgudan bir cevap beklemeden fonksiyon bitmektedir. Cevap geldiğinde ise web kancası servisi linkinde belirtilen adrese gönderilmektedir. Bunun için de Replicate servisinin bize oluşturduğu UPA jetonu bilgisini kullanarak Replicate servisi ile bağlantı kurulmaktadır.

import os
import threading
import requests
import replicate


os.environ["REPLICATE_API_TOKEN"] = "YOUR_API_TOKEN"


def replicate_request(request):
    request_json = request.get_json()
    prompt = request_json.get('prompt', 'default_prompt')
    user_id = request_json.get('user_id', 'default_user_id')
    model = replicate.models.get("stability-ai/stable-diffusion")
    version = model.versions.get("27b93a2413e7f36cd83da926f3656280b2931564ff050bf9575f1fdf9bcd7478")
    prediction = replicate.predictions.create(
        version=version,
        input={"prompt": prompt, "user_id": user_id},
        webhook="https://example.com/your-webhook",
        webhook_events_filter=["completed"])


    return {"message": "Input  sended to replicate."}

AWS S3’ye Kaydet Fonksiyonu Dönüşümü

Google Depolama Servisi üzerinden öncelikle bir bucket oluşturuyoruz. Kaydedeceğimiz görüntüler bu bucket içerisine kayıt edilecektir. Bu işlemler için aşağıdaki “Firebase Bulut Depoalama – Gelen Resim Bilgisini PNG olarak kaydetme” fonksiyonunu oluşturmaktayız.İlk olarak, belirli bir Google Bulut Depolama kovası adı BUCKET_NAME tanımlanır. Ardından, download_image fonksiyonu, verilen bir URL’den bir görüntüyü indirir ve Pillow (PIL) kütüphanesini kullanarak bir görüntü nesnesine dönüştürür. convert_image_to_array fonksiyonu, Pillow kütüphanesini kullanarak bir görüntü nesnesini NumPy dizisine dönüştürür. upload_to_gcs fonksiyonu, bu NumPy dizisini Google Bulut Depolama’ya yükler. Bu işlem için geçici bir dosya oluşturulur, görüntü bu dosyaya kaydedilir, ardından Google Bulut Depolama’ya yüklenir ve geçici dosya silinir. Son olarak, process_replicate_webhook fonksiyonu, bir giriş isteği alır ve bu isteği işleyerek görüntüyü indirir, işler, Google Bulut Depolama’ya yükler. Replicate UPA sorgusundan gelen resim url’i ve jeton bilgisi ise bildirim gönderen yeni oluşturacağımız bulut fonksiyonuna gönderilmektedir

import os
import tempfile
from google.cloud import storage
from PIL import Image
import numpy as np
from io import BytesIO
import requests
import json


BUCKET_NAME = 'your_bucket_name'


def download_image(image_url):
    print("url:",image_url)
    response = requests.get(image_url)
    print("url:",image_url)
    image = Image.open(BytesIO(response.content))
    return image


def convert_image_to_array(image):
    return np.array(image)


def upload_to_gcs(bucket_name, user_id, image_array):
    client = storage.Client()
    bucket = client.bucket(bucket_name)


    _, temp_filename = tempfile.mkstemp()
   
    Image.fromarray(image_array).save(temp_filename, format='PNG')


    destination_blob_name = f"{user_id}/image.png"


    blob = bucket.blob(destination_blob_name)
    blob.upload_from_filename(temp_filename)


    os.remove(temp_filename)


    return f"gs://{bucket_name}/{destination_blob_name}"


def process_replicate_webhook(request):
    request_json = request.get_json()
    print("request:",request_json)


    if not request_json:
        return {"error": "Invalid JSON payload"}


    user_id = request_json['input']['user_id']
    status = request_json['status']
    image_url = request_json['output'][0]


    if not image_url or not user_id:
        return {"error": "Missing image_url or user_id"}


    image = download_image(image_url)


    image_array = convert_image_to_array(image)


    gcs_url = upload_to_gcs(BUCKET_NAME, user_id, image_array)
    print("Received Replicate webhook:", user_id, status, image_url)
    headers = {
        "Content-Type": "application/json",
    }
    push_notificaiton_payload = {
        "token": user_id,
        "image_url": image_url
    }
    response_push_notification = requests.post("your_notification_function_url", data=json.dumps(push_notificaiton_payload), headers=headers)
    return {"message": f"Image saved to {gcs_url}"}

Firebase Bildirim Gönderme Fonksiyonu

“Firebase Bulut Bildirimi – Kullanıcıya Bildirim ile Sonuç Resmini Gönderme” kodu, Firebase Bulut Mesajlaşma ve Flask web uygulama çerçevesini kullanarak bir bildirim gönderme işlemini gerçekleştirir. İlk olarak, Firebase projesine erişim sağlamak için bir firebase_key.json dosyasından alınan kimlik bilgileriyle bir Firebase uygulaması başlatılır. send_notification fonksiyonu, gelen HTTP isteğini işler ve istekte bulunan cihazın FBM jetonunu ve bir görüntü URL’sini alır. Eğer FBM jetonu eksikse, hata mesajı ile birlikte 400 Geçersiz İstek durumu döner. Ardından, bir messaging.Message nesnesi oluşturulur, bu nesne FBM jetonu , bildirim içeriği ve görüntü URL’sini içerir. Bu bildirim, başlık, gövde ve tıklama eylemi ile özelleştirilmiştir. Son olarak, bildirim gönderme işlemi messaging.send(message) ile gerçekleştirilir. Başarılı bir gönderim durumunda başarı mesajı ve HTTP durumu 200 döner. Herhangi bir hata durumunda ise hatayı içeren bir mesaj ve HTTP durumu 500 döner.

import json
import firebase_admin
from firebase_admin import credentials, messaging
from flask import request




cred = credentials.Certificate('firebase_key.json')
firebase_admin.initialize_app(cred, options={'senderId': 'your_sender_id'})
def send_notification(request):


  request_json = request.get_json()
  image_url = request_json.get('image_url', '')
  registration_token = request_json.get('token', '')




  if not registration_token:
    return 'Missing FCM token in the request', 400




  message = messaging.Message(
      token=registration_token,
      data={'click_action': image_url,
            'title':'Resminiz Hazır',
            'body':'Resminiz Hazır Indirmek Için Tıklayın!'},  
  )
 
  try:


    response = messaging.send(message)
    return f'Successfully sent message: {response}', 200
  except Exception as e:
    return f'Error sending message: {str(e)}', 500

Gönderilen bildirimin kullanıcı tarafında kullanıcıya gösterilmesi için firebase-messaging-sw.js dosyasına aşağıdaki “Javascript Bildirim Alma ve Kullanıcıya Gösterme” kod bloğunu eklememiz gerekmektedir. Bu kod, bir web tarayıcısında çalışan bir arka plan servisini temsil eder. messaging.onBackgroundMessage fonksiyonu, tarayıcı arka planda iken gelen bildirim mesajlarını dinler ve taşıma verisi(payload) içindeki verileri işler. Bu veriler arasında bildirim başlığı (notificationTitle) ve tıklama eylemi (click_action) bulunmaktadır. self.registration.showNotification fonksiyonu, tarayıcıda bir bildirim gösterir. Gösterilen bildirimin başlığı notificationTitle olarak, ve tıklama eylemi click_action olarak belirlenir. self.addEventListener('notificationclick', function(event) { ... }); kısmı, kullanıcının bir bildirime tıkladığında yapılacak işlemleri belirler. Tıklanan bildirim kapatılır ve eğer daha önce tıklanan URL (lastClickedURL) şu anki tıklanan URL ile aynı değilse, yeni bir pencere açılır ve payload.data.click_action URL’sine yönlendirilir. Ayrıca, tıklanan URL, en son tıklanan URL olarak lastClickedURL değişkenine atanır. Bu kod, arka planda çalışan bir servis aracılığıyla gelen FBM bildirimlerini dinler, bildirimleri gösterir ve kullanıcının tıkladığı zaman belirli bir URL’ye yönlendirir.

let lastClickedURL = '';


messaging.onBackgroundMessage(function(payload) {
    console.log('Received background message ', payload);


const notificationTitle = payload.data.title;


const notificationOptions = {
        click_action: payload.data.click_action,
        notificationTitle: notificationTitle,
    };


    self.registration.showNotification(notificationTitle, notificationOptions);
    console.log('test2', payload.data.click_action);


    self.addEventListener('notificationclick', function(event) {
        const clickedNotification = event.notification;
        clickedNotification.close();


        if (payload.data.click_action !== lastClickedURL) {
            event.waitUntil(
                clients.openWindow(payload.data.click_action)
            );
            lastClickedURL = payload.data.click_action;
        }
    });
});

Demo

Şekil 6 Kullanıcıdan Girdi Olarak Alınan Metin Bilgisi Submit Butonu ile Firebase Bulut Fonksiyonuna Gönderilir
Şekil 7 Firebase Bulut Fonksiyonundan Kullanıcının Cihazına Bildirim Gönderir
Şekil 8 Demo Replicate Stablediff tarafından Üretilen Resim Sonucu

Sonuç


Bu yazıda, Flask kullanarak geliştirilen bir web uygulamasının Firebase ve AWS arasında geçiş yapma sürecini açıkladım. AWS’deki bir akışın, Firebase servisleri kullanılarak nasıl uyarlandığını detaylı bir şekilde anlattım. Firebase ile geçişte, öncelikle Flask uygulamasına Firebase Bulut Mesajlaşma entegrasyonu eklendi. Kullanıcıya bildirim gönderebilmek için kullanıcı tarafında bir yapı oluşturuldu ve FBM jetonu alınarak Flask uygulamasına iletildi. Ardından, AWS Lambda fonksiyonları yerine Google Bulut Fonksiyonları kullanılarak Replicate UPA’sına sorgu gönderme işlemleri Firebase üzerinde gerçekleştirildi. Gelen cevaplar Google Bulut Depolama’ya kaydedildi ve bu süreç Firebase Bulut Fonksiyonları ile yönetildi.Son olarak, kullanıcıya gönderilen resim URL’si ve FBM jetonu ile birlikte Firebase Bulut Mesajlaşma üzerinden bir bildirim gönderme işlemi gerçekleştirildi. Bu adımların detaylı açıklamalarıyla birlikte, geçiş süreci adım adım anlatıldı ve örnek kod parçacıkları ile desteklendi.