Flutter ile NoSQL Veritabanı Kullanan To Do App Yapımı

z1lo

Üye
16 Haz 2022
53
39
bu konuda Flutter ile to do app yapımını detaya girmeden başlangıç seviyesinde olmayan kişilerin incelemesi için lokal veritabanı kullanımı açısından iyi bir örnek proje anlatacağım öncelikle yapacağım örnek uygulamanın özellikleri:
  • Hive lokal veritabanını kullanarak görevleri uygulama kapansa da hatırlaması​
  • Efektif kodlama (tek satırla NoSQL veritabanını değiştirebilme örneğin Hive veritabanı yerine Secure Storage vb geçişlerin tek satırda yapılabilmesi)​
  • Sade tasarım​
  • Görevlerin kaydırılarak silinebilmesi​
  • Görevler arasında arama yapılabilmesi​
gibi özellikler olacak uygulamamıza başlayalım.

son hal:

vp8Un.png


arama yapılabilir halde olacak:

vpKHy.png

öncelikle pubspec.yaml'a eklenmesi gereken package'lar:
Kod:
cupertino_icons: ^1.0.2

  flutter_datetime_picker: ^1.5.1

  uuid: ^3.0.7

  intl: ^0.18.0

  hive: ^2.2.3

  hive_flutter: ^1.1.0

  get_it: ^7.2.0



dev_dependencies:

  flutter_test:

    sdk: flutter



  # The "flutter_lints" package below contains a set of recommended lints to

  # encourage good coding practices. The lint set provided by the package is

  # activated in the `analysis_options.yaml` file located at the root of your

  # package. See that file for information about deactivating specific lint

  # rules and activating additional ones.

  flutter_lints: ^2.0.0

  hive_generator: ^2.0.0

  build_runner: ^2.3.3

projemiz lib altında bu şekilde klasör yapısı:

vpYsD.png


kodların yanına mümkün olduğunca // açıp açıklamaya çalıştım

main.dart ile başlayalım:

Kod:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get_it/get_it.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:to_do_project/data/local_storage.dart';
import 'package:to_do_project/models/task_model.dart';
import 'package:to_do_project/pages/home_page.dart';

final locator = GetIt.instance; //global bir locator nesnesi tanımladık get_it package kullanarak

void setup(){
  locator.registerSingleton<LocalStorage>(HiveLocalStorage());
  //yarın öbür gün hive yerine başka bir sey kullanırsak hivelocalstorage kısmını degistirecegiz mantık bu
}

Future<void> setupHive() async{ //Hive ayarlarımızı şöyle bir topladık
  await Hive.initFlutter(); //runAppden önce Hive ı başlatıyoruz
  Hive.registerAdapter(TaskAdapter()); //adapterımızı da register etmiş olduk
  var taskBox = await Hive.openBox<Task>('tasks'); //icinde taskların bulundugu kutu açılmış oluyor artık
  for (var task in taskBox.values) {
    if(task.createdAt.day != DateTime.now().day){
     taskBox.delete(task.id);
    }
  }
}

void main() async{
  //mainde uzun süren runappden önce çalışmasını istediğimiz şeyler için
  WidgetsFlutterBinding.ensureInitialized();


  //status barı kaldırmak
  SystemChrome.setSystemUIOverlayStyle(
    const SystemUiOverlayStyle( //durum cubugu ile ilgili stil bilgileri icin
      statusBarColor: Colors.transparent, //durum cubugu rengini beyaz yaptık
    ),
  );

  await setupHive();
  setup();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData( //themedata ile uygulamanın temasını renkler vb şeylerini themedata diyerek merkezi yonetim yapılabiliyor
        primarySwatch: Colors.blue,
        appBarTheme: const AppBarTheme( //appbartemasını buradan ayarlayabiliyoruz
          elevation: 0,
          backgroundColor: Colors.white, //arkaplan beyaz olsun dedik
          iconTheme: IconThemeData(  //iconların temasını ayarlayabiliriz bu şekilde
            color: Colors.black, //iconların rengi siyah olsun dedik
          ),
        ),
      ),
      home: const HomePage(),
    );
  }
}

model sınıfımızı oluşturalım task_model sınıfı daha sonra Hive package kullandığımız için otomatik olarak .g lisi oluşuyor

Kod:
/*
* bu sınıf bir model sınıftır tasklar yani uygulamamızdaki görevler için oluşturulmuş bir model sınıftır
*
* */


import 'package:hive/hive.dart';
import 'package:uuid/uuid.dart';
part 'task_model.g.dart';

@HiveType(typeId: 1)
class Task extends HiveObject{
  //Klasik öğrendiğimiz hive a çevirme işlemini yapıyoruz fakat burada ekstradan extends hiveobject dedik bunun da faydası şu
  //update ve delete işlemleri çok daha kolay oluyor extends HiveObject dersek.

  @HiveField(0)
  late final String id;

  @HiveField(1)
  late String name;

  @HiveField(2)
  late final DateTime createdAt;

  @HiveField(3)
  late bool isCompleted;

  //sınıfımızın constructor'ını oluşturduk
  Task(
      {required this.id,
      required this.name,
      required this.createdAt,
      required this.isCompleted});


  /*
  factory kullanımını hatırlayalım.
  factory default constructor'dan farklı olarak bu sınıftan nesne üretmek için kullanılan bir fonksiyon gibidir.
  yani Task.create yaptığımızda şu şu parametreyi isteyip return diyerek bir nesne döndürüyoruz istediğimiz ayarda
   */
  factory Task.create({required String name, required DateTime createdAt}) {
    return Task(
        id: const Uuid().v1(),
        name: name,
        createdAt: createdAt,
        isCompleted: false);
  }
}

home_page sayfamızı oluşturalım main.dart içerisinde direkt olarak yazmak yerine home_page diye bir stful yapı oluşturup oraya tasarımımızı yapıyoruz

Kod:
import 'package:flutter/material.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:to_do_project/data/local_storage.dart';
import 'package:to_do_project/main.dart';
import 'package:to_do_project/models/task_model.dart';
import 'package:to_do_project/widgets/custom_search_delegate.dart';
import 'package:to_do_project/widgets/task_list_item.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late List<Task>
      _allTasks; //Task sınıfından nesneler tutabilen bir List oluşturduk

  late LocalStorage _localStorage;

  /*initState
  * burada initState metodu sadece bir kez çalışır uygulama başlatıldığında
  * çalıştığında olacakları yazıyoruz bir kez çalışır sadece bu metod
  * */
  @override
  void initState(){
    // TODO: implement initState
    super.initState();
    _localStorage = locator<LocalStorage>(); //locatori kullanarak localstorage turunde bir veri bekledigimizi belirttik
    _allTasks = <Task>[]; //allTasks adlı List'e boş bir atama yaptık
    _getAllTaskFromDb();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: GestureDetector(
          onTap: () {
            _showAddTaskBottomSheet(context); //metodu kullanıyoruz
          },
          child: const Text(
            "Bugün Neler Yapacaksın",
            style: TextStyle(color: Colors.black),
          ),
        ),
        centerTitle: false,
        actions: [
          IconButton(onPressed: () {
            _showSearchPage();
          }, icon: const Icon(Icons.search)),
          IconButton(
              onPressed: () {
                _showAddTaskBottomSheet(context);
              },
              icon: const Icon(Icons.add)),
        ],
      ),
      /*
      * Tasarım olarak body kısmına bir ListView.builder tasarımı düşündük
      * ListView.builder bize context, index veriyor index 0 dan başlar öyle gider
      * bir değişken oluşturduk oankiTaskIndex diye onu da allTasks listesinin 0.indexi olsun dedik o öyle gider 0,1,2 diye
      * ListView.builder'ın uzunluğunu da belirleyecek olan şey zaten allTasks listesinin uzunluğu kadar olacak öyle ayarladık
      * sonra her seferinde ListTile döndürecek her bir index için çünkü ListView.builder'ın olayı bu
      *
      * başlangıçta isNotEmtpy falan yapmamızın sebebi eğer _allTasks listesi boş değilse yani bir şeyler varsa tasarımı göster
      * yook eğer bir şeyler yoksa yani boşsa : dan sonra yazdığımız Center içerisinde bir Text widget gösterecek hadi görev ekle diye
      * */
      body: _allTasks.isNotEmpty
          ? ListView.builder(
              itemBuilder: (context, index) {
                var _oankiTaskElemani = _allTasks[index];
                /*
          * Dismissible çok güzel bir widget sağa kaydırmaya yarıyor içerisindeki widgetı
          * sağa kaydırıldığında onDismissed özelliği var oraya direction yazmak yeterli
          * onDismissed edildiğinde olacakları yazdık _allTasks listemizin o anki elemanini sil demiş olduk listeden
          * Dismissible widgetı bizden bir key bekler her seferinde farklı olmalı bu key. Bu keye de oankielemanin id sini verdik
          * */
                return Dismissible(
                  background: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: const [
                      Icon(
                        Icons.delete,
                        color: Colors.grey,
                      ),
                      SizedBox(
                        width: 8,
                      ),
                      Text("Bu görev silindi"),
                    ],
                  ),
                  key: Key(_oankiTaskElemani.id),
                  onDismissed: (direction){
                    _allTasks.removeAt(index);
                    _localStorage.deleteTask(task: _oankiTaskElemani);
                    setState(() {});
                  },
                  child: TaskItem(task: _oankiTaskElemani,), //oankiTaskElemanini TaskItem sınıfından nesne oluşturuyoruz
                  /*
                  * TaskItem sınıfı bizden bir Task sınıfı türünde nesne bekliyor zaten
                  * biz de oankiTaskElemani ni yolladık
                  * TaskItem sınıfına gidip orada neler olduğuna bakabilirsin*/
                );
              },
              itemCount: _allTasks.length,
            )
          : const Center(
              child: Text("Hadi görev ekle"),
            ),
    );
  }

  //goreveklebottomı adını bu sekilde ing koydugumuz bir metod olusturduk
  void _showAddTaskBottomSheet(BuildContext context) {
    showModalBottomSheet(
      //guzel bir widget biraz karartarak alltan bir pencere acıyor gibi
      context: context,
      builder: (context) {
        return Container(
          //bir tasarım döndürüyoruz
          /*
          * bu tasarımda bir container koyduk öncelikle containera padding verdik yani içindeki itemlerle arasında
          * kendi arasında boşluk bırakması için. burada şöyle bir ayar var
          * MediaQuery den yararlanıyoruz viewInsets diye bir ozelligi var viewInsets.bottom ifadesi bize
          * klavye açıldığında klavyenin kapladığı alanı veriyor bizde bu şekilde padding verdiğimizde
          * klavye açıldığında Containerımız ve içerisindeki her şey tabiki alttan o kadar yukarı çıkıyor baya güzel*/
          padding:
              EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
          width: MediaQuery.of(context).size.width,
          child: ListTile(
            title: TextField(
              autofocus: true, //direkt olarak imlecin yazilabilir halde olmasini sagliyor
              style: const TextStyle(fontSize: 20),
              decoration: const InputDecoration(
                hintText: "Görev Nedir ?",
                border: InputBorder
                    .none, //InputBorder.none dedik çünkü altta çizgi vardı o kalktı
              ),
              onSubmitted: (value) {
                //görev nedir text kutucuguna bir seyler yazildıktan klavyeden tamama basınca
                Navigator.of(context).pop(); //geri dön demek

                if (value.length > 3) {
                  /*
                * flutter date time picker adlı package ı yükledik zaman tarih vb. seçme penceresi açıyor bize*/
                  DatePicker.showTimePicker(
                    context,
                    showSecondsColumn: false,
                    onConfirm: (time) async{
                      //yeni görev ekliyoruz
                      /*
                      * burada yapılan şey şu Task diye bir sınıf var bu sınıfın factory metoduyla nesne üretiyoruz
                      * bu sınıf da zaten Görev sınıfı yani oluşturduğumuz model sınıftan nesne üretiyoruz mevzu bu
                      * sonra bu nesneyi ürettiğimiz bu nesneyi allTasks diye bir listin içine atıyoruz olay bu*/
                      var addNewTask =
                          Task.create(name: value, createdAt: time);
                      _allTasks.insert(0, addNewTask);
                      await _localStorage.addTask(task: addNewTask);
                      setState(() {});
                    },
                  ); //showSecondsColumn false yaptık ki saniye gözükmesin
                }
              },
            ),
          ),
        );
      },
    );
  }


  //bir tane fonksiyon olusturuyoruz allTasks e localStorage sınıfından getAllTask metodunu kullanacak sonra setstate çakacak
  void _getAllTaskFromDb() async{
    _allTasks = await _localStorage.getAllTask();
    setState(() {

    });
  }

  void _showSearchPage() async{ //arama ile ilgili işlemler icin
    await showSearch(context: context, delegate: CustomSearchDelegate(allTasks: _allTasks));
    _getAllTaskFromDb();
  }
}

itemlerin listelenmesi ile ilgili task_list_item.dart dosyamız

Kod:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:to_do_project/data/local_storage.dart';
import 'package:to_do_project/main.dart';
import 'package:to_do_project/models/task_model.dart';

class TaskItem extends StatefulWidget {
  Task task;

  TaskItem({Key? key, required this.task}) : super(key: key);

  @override
  State<TaskItem> createState() => _TaskItemState();
}

class _TaskItemState extends State<TaskItem> {
  final TextEditingController _taskNameController =
      TextEditingController(); //bir kontroller tanımladık textfield için

  late LocalStorage _localStorage;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _localStorage = locator<LocalStorage>();

  }

  @override
  Widget build(BuildContext context) {
    _taskNameController.text =
        widget.task.name; //sayfa açıldığında bu atama gerçekleşsin
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(8),
          boxShadow: [
            BoxShadow(color: Colors.black.withOpacity(.2), blurRadius: 10),
          ]),
      child: ListTile(
        leading: GestureDetector(
          onTap: () {
            widget.task.isCompleted = !widget.task.isCompleted;
            _localStorage.updateTask(task: widget.task);
            setState(() {});
          },
          child: Container(
            //bir container içine bir icon koyduk ve box decoration falan verdik
            decoration: BoxDecoration(
              color: widget.task.isCompleted ? Colors.green : Colors.white,
              border: Border.all(
                color: Colors.grey,
                width: 0.8,
              ),
              shape: BoxShape.circle,
            ),
            child: const Icon(
              Icons.check,
              color: Colors.white,
            ),
          ),
        ),
        title: widget.task.isCompleted
            ? Text(
                widget.task.name,
                style: const TextStyle(
                    decoration: TextDecoration.lineThrough, color: Colors.grey),
              )
            : TextField(
                controller: _taskNameController,
                minLines: 1,
                maxLines: null, //sığmama sorunu çözülüyor maksimum satırı null veriyoruz genisletiyoruz yani esneklik
                textInputAction: TextInputAction.done, //klavyede yeni alt satıra geç değil de tik işaretinin çıkması için
                decoration: const InputDecoration(border: InputBorder.none),
                onSubmitted: (yeniDeger){
                  if(yeniDeger.length>3){
                    //eger textfielda yazılan yazı 3 ten buyukse artık gelen nesnenin ismine bu yeni degeri ata klavyeden ok basılınca
                    widget.task.name = yeniDeger;
                    _localStorage.updateTask(task: widget.task);
                  }
                },
              ),
        trailing: Text(
          //intl package ile tarih formatlandırma yapabiliyoruz dokümantasyonda var bir seyler
          DateFormat('hh:mm a').format(widget.task.createdAt),
          style: const TextStyle(
            fontSize: 14,
            color: Colors.grey,
          ),
        ),
      ),
    );
  }
}

ve en önemli kısım olan lokal veritabanımızı abstract bir sınıftan türetme mantığı ile efektif bir şekilde lokal veritabanları arasında değişim yapılabilecek halde oluşturuyoruz ve metodların içerisini dolduruyoruz

Kod:
//öyle bir kod yazmalıyız ki lokal veritabanı değiştiğinde kodlarımız bundan etkilenmesin.

//soyut sınıf içindeki kodları yazmadıgımız yapılarla yapıyorduk bunu

import 'package:hive/hive.dart';
import 'package:to_do_project/models/task_model.dart';

abstract class LocalStorage{
  Future<void> addTask({required Task task}); //parametre olarak task sınıfından nesne alabilen metodumuz addTask metodu
  Future<Task?> getTask({required String id}); //string türünde bir id alan ve geriye Task sınıfından nesne döndüren metodumuz
  Future<List<Task>> getAllTask();  //Task sınıfından nesneleri tutabilen bir list döndüren getAllTask metodumuz
  Future<bool> deleteTask({required Task task}); //bool bir deger donduren parametre olarak Task sınıfından bir nesne isteyen metodumuz
  Future<Task> updateTask({required Task task}); //ve yine Task sınıfından nesne isteyen parametre olarak ve geriye bir Task sınıfından nesne döndüren metodumuz
}

class MockData extends LocalStorage{
  @override
  Future<void> addTask({required Task task}) {
    // TODO: implement addTask
    throw UnimplementedError();
  }

  @override
  Future<bool> deleteTask({required Task task}) {
    // TODO: implement deleteTask
    throw UnimplementedError();
  }

  @override
  Future<List<Task>> getAllTask() {
    // TODO: implement getAllTask
    throw UnimplementedError();
  }

  @override
  Future<Task> getTask({required String id}) {
    // TODO: implement getTask
    throw UnimplementedError();
  }

  @override
  Future<Task> updateTask({required Task task}) {
    // TODO: implement updateTask
    throw UnimplementedError();
  }
  //falanfilan flana filan mesela
} //mesela yani

class HiveLocalStorage extends LocalStorage{
  late Box<Task> _taskBox; //Task nesneleri tutabilen bir hive kutusu tanımladık

  HiveLocalStorage(){
    _taskBox = Hive.box<Task>('tasks'); //constructor da _taskBox a bu kutuyu koyduk mainde açtığımız şey bu aynı olmalı
  }

  @override
  Future<void> addTask({required Task task}) async{
   await _taskBox.put(task.id, task);
  }

  @override
  Future<bool> deleteTask({required Task task}) async{
    await task.delete(); //hiveobject sayesinde böyle kolayca yapıyoruz öbür türlü _taskBox.delete(task.id); şeklinde yazardık
    return true;
  }

  @override
  Future<List<Task>> getAllTask() async{
    List<Task> _allTask = <Task>[]; //Task nesneleri tutabilen boş bir list oluşturduk
    _allTask = _taskBox.values.toList(); //toList diyerek butun degerleri allTask listesine koyduk
    if(_allTask.isNotEmpty){ //eger allTask listesinin eleman sayisi 0 dan buyukse
      _allTask.sort((Task a, Task b)=> b.createdAt.compareTo(a.createdAt)); //kendi içinde siralama yap tarihlerine gore dedik
    }
    return _allTask;
  }

  @override
  Future<Task?> getTask({required String id}) async{
    if(_taskBox.containsKey(id)){
      return _taskBox.get(id);
    }else{
      return null;
    }
  }

  @override
  Future<Task> updateTask({required Task task}) async{
    await task.save();
    return task;
  }

} //bizim olayımız bu tabiki hive ı kullanacagız cok sistematik yaptık

son olarak arama muhabbetini çözdüğümüz custom_search_delegate.dart ımızı dolduralım

Kod:
import 'package:flutter/material.dart';
import 'package:to_do_project/data/local_storage.dart';
import 'package:to_do_project/main.dart';
import 'package:to_do_project/models/task_model.dart';

import 'task_list_item.dart';

class CustomSearchDelegate extends SearchDelegate {
  late final List<Task> allTasks;

  CustomSearchDelegate({required this.allTasks});

  @override
  List<Widget>? buildActions(BuildContext context) {
    //arama kısmının sağ tarafındaki ikonları
    return [
      IconButton(onPressed: () {
        query.isEmpty ? null : query =
        ''; // kullanıcının arama yapmak istedigi deger bossa sorguyu '' bosalt
      }, icon: const Icon(Icons.clear)),
    ];
  }

  @override
  Widget? buildLeading(BuildContext context) {
    //en bastaki ikonları
    return GestureDetector(
        onTap: () {
          close(context,
              null); //bunlar hep flutter sayesinde close metodu falan query mevzusu falan
        },
        child: const Icon(Icons.arrow_back_ios, color: Colors.red, size: 24,));
  }

  @override
  Widget buildResults(BuildContext context) {
    //cıkacak sonucların gosterilis sekli
    List<Task> filteredList = allTasks.where((gorev) =>
        gorev.name.toLowerCase().contains(query.toLowerCase())).toList();
    return filteredList.length > 0 ? ListView.builder(
      itemBuilder: (context, index) {
        var _oankiTaskElemani = filteredList[index];
        /*
          * Dismissible çok güzel bir widget sağa kaydırmaya yarıyor içerisindeki widgetı
          * sağa kaydırıldığında onDismissed özelliği var oraya direction yazmak yeterli
          * onDismissed edildiğinde olacakları yazdık _allTasks listemizin o anki elemanini sil demiş olduk listeden
          * Dismissible widgetı bizden bir key bekler her seferinde farklı olmalı bu key. Bu keye de oankielemanin id sini verdik
          * */
        return Dismissible(
          background: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: const [
              Icon(
                Icons.delete,
                color: Colors.grey,
              ),
              SizedBox(
                width: 8,
              ),
              Text("Bu görev silindi"),
            ],
          ),
          key: Key(_oankiTaskElemani.id),
          onDismissed: (direction) async{
            filteredList.removeAt(index);
            await locator<LocalStorage>().deleteTask(task: _oankiTaskElemani);

          },
          child: TaskItem(task: _oankiTaskElemani,), //oankiTaskElemanini TaskItem sınıfından nesne oluşturuyoruz
          /*
                  * TaskItem sınıfı bizden bir Task sınıfı türünde nesne bekliyor zaten
                  * biz de oankiTaskElemani ni yolladık
                  * TaskItem sınıfına gidip orada neler olduğuna bakabilirsin*/
        );
      },
      itemCount: filteredList.length,
    ) : const Center(child: Text("Aradığınızı bulamadık"),);
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    //kullanici bir harf falan yazarsa veya bir sey yazmazsa gosterilecekler
    return Container();
  }

}

hepsi bu kadar kodların içerisinde // ile açıklamalar mevcut bazı açıklamalar gereksiz veya anlatmak için yanlış denilebilecek düzeyde. kodlar tabii çok daha efektif hale getirilebilirdi çok daha fazla dosyalara parçalanarak ancak sadece örnek olması açısından daha çok lokal veritabanı kullanımına yönelik bir örnek bir projeydi.

vptGC.gif

[/CODE][/CENTER]
 
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.