diff --git a/.env.example b/.env.example index f52308be..2ef3ebea 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,7 @@ LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug DB_CONNECTION=mysql -# DB_HOST=mysql +# Replace to DB_HOST=mysql when run via `sail up` DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=secretic diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 06608e44..337cac63 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -19,10 +19,10 @@ jobs: password: ${{ secrets.SSH_PASSWORD }} script: | whoami - cd /usr/secretic + cd /var/www/web/sites/secretic.app git reset --hard HEAD git clean -f -d git pull origin main git show --summary - -# TODO ./vendor/bin/sail composer install + composer -V + composer install diff --git a/DEPLOY.md b/DEPLOY.md index 4fda8d8f..cdeb3e44 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -2,18 +2,47 @@ For simplicity, we will deploy the project to VDS. The deployment process consists of several steps: -## 1. Create a VDS on your preferred hosting +## Create a VDS on your preferred hosting -Create a VDS virtual machine for Docker, for example, at [NetAngels](https://panel.netangels.ru). +Create a VDS virtual machine for example at [NetAngels](https://panel.netangels.ru). -For [secretic.app](https://secretic.app) we used distribution `Docker` (Debian 11): +For [secretic.app](https://secretic.app) we used distribution `Ubuntu 20.04`. -![Docker VDS](vds-docker.png) +## Set up VDS -We will launch the project on [Laravel Sail](https://laravel.com/docs/9.x/sail) - yes, this is not ideal, but for non-production purposes it will do. +Login via SSH (as `root` user) and check PHP version (`php -v`). +If necessary, [upgrade to PHP 8](https://php.watch/articles/php-8.0-installation-update-guide-debian-ubuntu): -## 2. Generate RSA-key (for access to GitHub) +```bash +sudo apt update +sudo apt install software-properties-common +sudo apt update +sudo add-apt-repository ppa:ondrej/php +sudo apt update +sudo apt install php8.1-common php8.1-cli php8.1-mysql php8.1-xml php8.1-curl php8.1-bcmath php8.1-mbstring -y + ``` + +Manually [install](https://getcomposer.org/download/) Composer: + +```bash +php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" +php composer-setup.php +php -r "unlink('composer-setup.php');" +``` + +And make it available for calling through `composer`: + +```bash +sudo mv composer.phar /usr/local/bin/composer +``` + +## CHANGE USER! + +Next, we do everything from the user `web`, not `root`!!! + + +## Generate RSA-key (for access to GitHub) First, generate SSH-keys with `ssh-keygen`: @@ -46,10 +75,9 @@ The key's randomart image is: +----[SHA256]-----+ ``` +## Add RSA-key to GitHub -## 3. Add RSA-key to GitHub - -In the GitHub-repository, in the [Deploy Keys](https://github.com/gomzyakov/secretic/settings/keys) section, set the value of the public key from the virtual machine. +In the GitHub-repository, in the `Settings > Deploy Keys` section ([link](https://github.com/gomzyakov/secretic/settings/keys)), set the value of the public key from the virtual machine. You can get it via: @@ -59,77 +87,54 @@ cat ~/.ssh/id_rsa.pub This will allow deployment via `git pull` from VDS. -## 4. Set secrets in the repository on GitHub -After that, in the GitHub repository, in the `Settings > Secrets > Actions` section, set the values: +## Set secrets on GitHub + +After that, in the GitHub repository, in the `Settings > Secrets > Actions` section ([link](https://github.com/gomzyakov/secretic/settings/secrets/actions)), set the values: - `SSH_HOST`: This is the IP address of the server. -- `SSH_USERNAME`: This is the server username. +- `SSH_USERNAME`: This is the `web` username. - `SSH_PASSWORD`: This is the user password. -## 5. Clone repository from GitHub +## Clone repository from GitHub -Login to VDS via SSH as `root` user. +Go to path `/var/www/web/sites` and clone current repository (or your own fork). For example, use `secretic.app` folder name: -Go to path `/usr` and clone current repository (or your own fork). By default, Git clone to `secretic` folder: - -```bash -git clone git@github.com:gomzyakov/secretic.git && cd secretic -`````` + ```bash + cd /var/www/web/sites + git clone git@github.com:gomzyakov/secretic.git secretic.app + cd secretic + ``` -## 6. Init & run Laravel Sail on VDS +## Set up Laravel -Run the command for the first time: +Then go to path `/var/www/web/sites/secretic.app` and run some commands: ```bash -docker run --rm \ --u "$(id -u):$(id -g)" \ --v $(pwd):/opt \ --w /opt \ -laravelsail/php81-composer:latest \ -composer install --ignore-platform-reqs +php -r "file_exists('.env') || copy('.env.example', '.env');" +composer install +chmod -R 777 storage bootstrap/cache +php artisan key:generate ``` -Copy the environment settings: +- Write the correct database requisites in the `.env` file +- Create a `secretnotes` database via phpMyAdmin +- Run migrations `php artisan migrate:fresh --seed` -```bash -cp .env.example .env -``` - -And replace `DB_HOST` to `mysql` in `.env` (for local development). - -Set permissions for some directories: - -```bash -chmod -R 777 bootstrap/cache -chmod -R 777 ./storage/logs -chmod -R 777 ./storage/framework -``` - -Start containers with Sail: +Open assigned to VDS URL (like `dd`) in your favorite browser. Happy using Secretic! -```bash -./vendor/bin/sail up -d -``` - -After that go to shell: - -```bash -./vendor/bin/sail shell -``` -And run final commands: +## Optional: Make domain aliases (if needed) ```bash -./artisan key:generate -./artisan migrate:fresh --seed +cd /var/www/web/sites +ln -s secretic.app www.secretic.app +ln -s secretic.app secretic.ru +ln -s secretic.app secretnotes.ru ``` -Open VDS IP-address in your favorite browser. Happy using Secretic! - - -## 7. Optional: Set up HTTPS +## Optional: Set up HTTPS >No need if you are using mode `Flexible` on Cloudflare diff --git a/app/Http/Controllers/NoteController.php b/app/Http/Controllers/NoteController.php index be2fdaaa..d1e3a2a6 100644 --- a/app/Http/Controllers/NoteController.php +++ b/app/Http/Controllers/NoteController.php @@ -76,18 +76,16 @@ public function createNote( NoteCreateRequest $request, NotesRepository $notes_repository ): Application|View|ViewFactory { - abort(500); - -// $note = $notes_repository->create( -// $request->getText(), -// $request->getPassword() ? Hash::make($request->getPassword()) : null, -// $this->getExpirationDate($request->getExpirationDate()) -// ); -// -// return view('note.show-link', [ -// 'hide_footer' => true, -// 'note_url' => route('note.open_link', ['slug' => $note->slug]), -// ]); + $note = $notes_repository->create( + $request->getText(), + $request->getPassword() ? Hash::make($request->getPassword()) : null, + $this->getExpirationDate($request->getExpirationDate()) + ); + + return view('note.show-link', [ + 'hide_footer' => true, + 'note_url' => route('note.open_link', ['slug' => $note->slug]), + ]); } /** diff --git a/tests/Feature/Note/CreateNoteTest.php b/tests/Feature/Note/CreateNoteTest.php index 3dd782bd..5abd3367 100644 --- a/tests/Feature/Note/CreateNoteTest.php +++ b/tests/Feature/Note/CreateNoteTest.php @@ -18,71 +18,70 @@ public function test_create_with_min_params(): void route('note.create', ['text' => $text = $this->faker->text()]) ); - $response->assertStatus(500); - -// $response->assertSee('Secret note successfully created!'); -// $response->assertSee('Copy note to clipboard'); -// -// $note_slug = $this->getNoteSlugFromHTMLPage($response); -// -// $this->assertDatabaseHas('notes', ['slug' => $note_slug]); -// -// /** @var Note $note */ -// $note = Note::where('slug', $note_slug)->first(); -// -// $this->assertInstanceOf(Note::class, $note); -// $this->assertSame($text, Crypt::decryptString($note->text)); + + $response->assertSee('Secret note successfully created!'); + $response->assertSee('Copy note to clipboard'); + + $note_slug = $this->getNoteSlugFromHTMLPage($response); + + $this->assertDatabaseHas('notes', ['slug' => $note_slug]); + + /** @var Note $note */ + $note = Note::where('slug', $note_slug)->first(); + + $this->assertInstanceOf(Note::class, $note); + $this->assertSame($text, Crypt::decryptString($note->text)); } -// public function test_create_with_full_params(): void -// { -// $response = $this->post( -// route('note.create', [ -// 'text' => $text = $this->faker->text(), -// 'encrypt_password' => 'foo_bar', -// 'expiration_date' => '1_week', -// ]) -// ); -// -// $response->assertStatus(200); -// -// $note_slug = $this->getNoteSlugFromHTMLPage($response); -// -// $this->assertDatabaseHas('notes', ['slug' => $note_slug]); -// -// /** @var Note $note */ -// $note = Note::where('slug', $note_slug)->first(); -// -// $this->assertInstanceOf(Note::class, $note); -// $this->assertSame($text, Crypt::decryptString($note->text)); -// } -// -// public function test_empty_note(): void -// { -// $response = $this->post( -// route('note.create', ['text' => '']) -// ); -// -// // TODO Is this the correct response status? -// $response->assertStatus(302); -// } -// -// /** -// * @param TestResponse $response -// * -// * @return string -// */ -// private function getNoteSlugFromHTMLPage(TestResponse $response): string -// { -// // Get note URL from HTML page -// preg_match('//iu', $response->content(), $matches); -// $note_url = $matches[1]; -// -// // Get last word from URL after a slash -// preg_match('/[^\\/]+$/', $note_url, $matches); -// -// return $matches[0]; -// } + public function test_create_with_full_params(): void + { + $response = $this->post( + route('note.create', [ + 'text' => $text = $this->faker->text(), + 'encrypt_password' => 'foo_bar', + 'expiration_date' => '1_week', + ]) + ); + + $response->assertStatus(200); + + $note_slug = $this->getNoteSlugFromHTMLPage($response); + + $this->assertDatabaseHas('notes', ['slug' => $note_slug]); + + /** @var Note $note */ + $note = Note::where('slug', $note_slug)->first(); + + $this->assertInstanceOf(Note::class, $note); + $this->assertSame($text, Crypt::decryptString($note->text)); + } + + public function test_empty_note(): void + { + $response = $this->post( + route('note.create', ['text' => '']) + ); + + // TODO Is this the correct response status? + $response->assertStatus(302); + } + + /** + * @param TestResponse $response + * + * @return string + */ + private function getNoteSlugFromHTMLPage(TestResponse $response): string + { + // Get note URL from HTML page + preg_match('//iu', $response->content(), $matches); + $note_url = $matches[1]; + + // Get last word from URL after a slash + preg_match('/[^\\/]+$/', $note_url, $matches); + + return $matches[0]; + } // TODO test 65K+ characters // TODO test_too_short_password diff --git a/vds-docker.png b/vds-docker.png deleted file mode 100644 index dd800cb9..00000000 Binary files a/vds-docker.png and /dev/null differ