Я действительно взволнован Laravel Spark . К тому времени, когда вы прочитаете это, вероятно, будет множество постов, объясняющих, как вы можете настроить это. Это не так интересно для меня, как путешествие, которое я собираюсь предпринять для создания реального бизнеса со Spark!
Идея проста. Я создал модуль Pagekit, который вы можете использовать для резервного копирования и восстановления данных сайта. Модуль позволяет легко сохранять и загружать эти резервные копии, а также восстанавливать их на разных серверах.
Проблема в том, что получение этих файлов резервных копий на удаленный сервер занимает много времени и немного хлопот. Мне часто хотелось, чтобы можно было быстро и безболезненно перенести это состояние приложения с одного сервера на другой и сделать автоматическое резервное копирование за пределы площадки. Поэтому я собираюсь настроить это для себя, и, возможно, другие найдут это достаточно полезным, чтобы заплатить за это.
Начиная
Я использую Stripe и намерен иметь единый план без проб. Настройка для этого довольно проста, но я запомнил идентификатор плана. Мне нужно это, чтобы настроить план в Spark …
Затем я сбрасываю свои секретные и открытые ключи Stripe и обновляю до последней версии API (через тот же экран, https://dashboard.stripe.com/account/apikeys ).
 Я забыл, что настройки в .env 
У Spark есть несколько ожидаемых полей регистрации / профиля, но я хочу добавить еще несколько. Я хотел бы спросить пользователей, хотят ли они автоматическое резервное копирование, и я также хотел бы получить их платежный адрес, чтобы я мог указать его в своем счете. Сначала я должен создать миграцию для нового поля:
 php artisan make:migration add_should_backup_field
Для этого мы можем добавить столбец (убедитесь, что удалили его, если миграция откатывается):
 use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddShouldBackupField extends Migration
{
    public function up()
    {
        Schema::table("users", function (Blueprint $table) {
            $table->boolean("should_backup");
        });
    }
    public function down()
    {
        Schema::table("users", function (Blueprint $table) {
            $table->dropColumn("should_backup");
        });
    }
}
  Сбор платежных адресов так же прост, как один вызов метода, в app/Providers/SparkServiceProvider.php 
 /**
 * @inheritdoc
 */
public function booted()
{
    Spark::useStripe();
    Spark::collectBillingAddress();
    Spark::plan("[Stripe plan name]", "[Stripe plan ID]")
        ->price(4.99)
        ->features([
            "Backups"
        ]);
}
Это добавляет поля адреса выставления счета к форме регистрации:
  Не забудьте настроить остальную часть SparkServiceProvider.php 
Хранение резервных копий
  Весь смысл этого приложения в том, чтобы дать авторам контента возможность загружать, перечислять и загружать свои резервные копии.  Далее я создам модель Backup 
 php artisan make:migration create_backups_table
php artisan make:model Backups
Таблица резервных копий нуждается в нескольких полях:
- Имя каждой резервной копии (возможно, содержит имя сайта, дату и включает ли оно базу данных и данные для загрузки).
- Загруженный файл резервной копии.
- Размер каждой резервной копии.
- Поле для связи резервной копии с пользователем, который ее создал.
Вот как выглядит настроенный файл миграции:
 use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
class CreateBackupsTable extends Migration
{
    public function up()
    {
        Schema::create("backups", function (Blueprint $table) {
            $table->increments("id");
            $table->string("name");
            $table->string("file");
            $table->integer("size");
            $table->integer("user_id");
            $table->timestamps();
        });
    }
    public function down()
    {
        Schema::dropIfExists("backups");
    }
}
Учитывая эту структуру базы данных, я хочу предоставить набор конечных точек JSON, с помощью которых пользователи могут просматривать и управлять резервными копиями. Опять же, мы можем использовать команду make, чтобы начать работу:
 php artisan make:controller BackupsController --resource
Spark специально разработан, чтобы стать хорошей отправной точкой для создания аутентифицированного API. Когда вы входите в систему с учетной записью Spark, вы можете перейти в раздел, в котором вы можете создавать токены API:
С помощью этого токена и создав контроллер ресурсов, я могу заставить метод index отображать некоторый отладочный текст. Тогда я могу сделать к нему запросы curl:
 curl -XGET http://localhost:8000/api/backup
  Чтобы это работало, мне нужно определить маршрут API (в app/Http/api.php 
 Route::group([
    "prefix" => "api",
    "middleware" => "auth:api"
], function () {
    Route::resource("backup", "BackupsController");
});
  Поскольку я не предоставил параметр GET api_token  Если я вошел в систему в то время, я перенаправлен обратно на экран приборной панели. 
  Если я определяю параметр api_token 
Я выбрал простой механизм загрузки, который очень мало проверяет файлы (пока я работаю с прототипом). Я хочу уточнить это позже с некоторой проверкой, но сейчас у меня есть:
 public function store(Request $request)
{
    $file = $request->file("file");
    $name = $request->get("name");
    if (!$file || !$name) {
        return [
            "status" => "error",
            "error" => "file or name missing",
        ];
    }
    $fileExtension = "." . $file->getClientOriginalExtension();
    $fileName = str_slug(time() . " " . $name) . $fileExtension;
    try {
        $file->move(storage_path("backups"), $fileName);
    } catch (Exception $exception) {
        return [
            "status" => "error",
            "error" => "could not store file",
        ];
    }
    $backup = Backup::create([
        "name" => $name,
        "file" => $fileName,
        "size" => $file->getClientSize(),
        "user_id" => Auth::user()->id,
    ]);
    return $backup;
}
  Я могу проверить это, создав пустой файл резервной копии и затем отправив его в store 
 touch backup.zip
curl
    -X POST
    -F name="My First Backup"
    -F file=@backup.zip
    "http://localhost:8000/api/backup?api_token=[my token]"
Я получаю обратно что-то похожее:
 {
    "name": "My First Backup",
    "file": "1461802753-my-first-backup.zip",
    "size": 0,
    "user_id": 1,
    "updated_at": "2016-04-28 00:19:13",
    "created_at": "2016-04-28 00:19:13",
    "id": 8
}
Обратите внимание, как файл называется по-разному, чтобы избежать коллизий? Он на самом деле назван в честь имени резервной копии, а не исходного имени файла, и я добавил временную метку, чтобы можно было загружать резервные копии с одинаковыми именами без конфликта имен.
Существует еще условие гонки, когда пользователи загружают резервные копии с одинаковым именем в одну секунду. Риск этого меньше, и я готов иметь дело с этим, когда это становится проблемой.
  Теперь я могу настроить действие index 
 public function index()
{
    return [
        "status" => "ok",
        "data" => Auth::user()->backups,
    ];
}
  Мне также пришлось создать эти отношения между пользователями и резервными копиями, добавив метод backupsUser 
 public function backups()
{
    return $this->hasMany(Backup::class);
}
Загрузка резервных копий
Я загружаю резервные копии в непубличную папку по причине. Я не хочу, чтобы пользователи могли угадывать (независимо от того, насколько маловероятно, что они догадаются правильно), как получить доступ к резервным копиям других пользователей.
  Поэтому я должен создать специальную конечную точку загрузки.  Я могу, однако, отыграть от действия show 
 public function show($id)
{
    $backup = Auth::user()->backups()->findOrFail($id);
    $path = storage_path("backups/". $backup->file);
    return response()->download($path, $backup->file);
}
Из-за того, как определяется область действия запроса, пользователям будет разрешено загружать только резервные копии, связанные с их учетной записью, и эти резервные копии не будут общедоступными за пределами этого одного маршрута.
Вывод
За невероятно короткий промежуток времени мне удалось использовать Spark для создания хостинговой платформы на основе подписки с аутентифицированным API для хранения и загрузки файлов резервных копий.
Предстоит проделать еще много работы, начиная от приложения и заканчивая повышением безопасности, внедрением ограничений хранилища и удалением резервной копии. Тем не менее, я воодушевлен началом, которое я сделал, и я могу вернуться к этому приложению в будущих уроках, так как я узнаю больше о Spark!
У вас есть какие-либо вопросы по поводу этого кода или комментарии о том, что бы вы сделали по-другому? Дайте нам знать об этом в комментариях!




