├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── Makefile ├── README.md ├── app ├── Console │ └── Commands │ │ └── ResetCommand.php ├── Http │ └── Controllers │ │ ├── ArticleController.php │ │ ├── Controller.php │ │ └── DictionaryController.php ├── Models │ ├── Article.php │ ├── Category.php │ ├── Comment.php │ ├── Dictionary.php │ ├── Setting.php │ └── User.php ├── MoonShine │ ├── Actions │ │ └── ArticleMassActive.php │ ├── Components │ │ └── DemoVersionComponent.php │ ├── Forms │ │ └── LoginForm.php │ ├── Http │ │ ├── Controllers │ │ │ ├── ArticleController.php │ │ │ └── ProfileController.php │ │ └── Requests │ │ │ └── ArticleMassActiveFormRequest.php │ ├── Layouts │ │ └── MoonShineLayout.php │ ├── Pages │ │ ├── Article │ │ │ ├── ArticleDetailPage.php │ │ │ ├── ArticleFormPage.php │ │ │ └── ArticleIndexPage.php │ │ ├── Category │ │ │ └── CategoryIndexPage.php │ │ ├── Dashboard.php │ │ ├── Dictionary │ │ │ ├── DictionaryDetailPage.php │ │ │ ├── DictionaryFormPage.php │ │ │ └── DictionaryIndexPage.php │ │ ├── ProfilePage.php │ │ └── SettingPage.php │ ├── Resources │ │ ├── ArticleResource.php │ │ ├── CategoryResource.php │ │ ├── CommentResource.php │ │ ├── DictionaryResource.php │ │ ├── MoonShineUserResource.php │ │ ├── MoonShineUserRoleResource.php │ │ ├── SettingResource.php │ │ └── UserResource.php │ └── Sets │ │ └── DashboardTableWithForm.php ├── Policies │ ├── ArticlePolicy.php │ ├── CategoryPolicy.php │ ├── DictionaryPolicy.php │ ├── MoonshineUserPolicy.php │ └── MoonshineUserRolePolicy.php └── Providers │ ├── AppServiceProvider.php │ ├── MoonShineServiceProvider.php │ └── RouteServiceProvider.php ├── artisan ├── bootstrap ├── app.php ├── cache │ └── .gitignore └── providers.php ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── cache.php ├── database.php ├── debugbar.php ├── filesystems.php ├── logging.php ├── mail.php ├── moonshine.php ├── queue.php ├── services.php └── session.php ├── database ├── .gitignore ├── factories │ ├── ArticleFactory.php │ ├── CategoryFactory.php │ └── UserFactory.php ├── migrations │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2014_10_12_100000_create_password_resets_table.php │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ ├── 2019_12_14_000001_create_personal_access_tokens_table.php │ ├── 2022_09_14_083721_create_articles_table.php │ ├── 2022_09_14_083733_create_dictionaries_table.php │ ├── 2022_09_14_164307_create_categories_table.php │ ├── 2022_10_30_143621_create_article_category_table.php │ ├── 2022_11_08_125431_add_slugs.php │ ├── 2022_11_13_103940_add_author_id_column_to_articles.php │ ├── 2022_11_13_103940_add_seo_columns_to_articles.php │ ├── 2023_01_06_094236_create_comments_table.php │ ├── 2023_01_07_184655_create_jobs_table.php │ ├── 2023_03_18_182739_add_new_columns.php │ ├── 2023_03_21_153639_add_new_column_to_comments.php │ ├── 2023_06_12_101633_create_settings_table.php │ ├── 2023_06_12_101646_category_sorting.php │ └── 2024_11_03_100651_create_notifications_table.php └── seeders │ └── DatabaseSeeder.php ├── docker-compose.yml ├── docker ├── dockerfiles │ ├── mysql │ │ ├── Dockerfile │ │ └── config │ │ │ ├── create_database.sql │ │ │ └── mysql.cnf │ ├── nginx │ │ ├── Dockerfile │ │ └── config │ │ │ └── nginx-php.conf │ ├── node │ │ └── Dockerfile │ └── php │ │ ├── Dockerfile │ │ └── config │ │ ├── php-dev.ini │ │ └── php-fpm.conf └── volumes │ └── Nginx │ └── logs │ ├── error.log │ └── moonshine-demo.error.log ├── lang ├── en │ └── demo.php └── vendor │ └── moonshine │ ├── en │ ├── auth.php │ ├── pagination.php │ ├── ui.php │ └── validation.php │ └── ru │ ├── auth.php │ ├── pagination.php │ ├── ui.php │ └── validation.php ├── package-lock.json ├── package.json ├── phpunit.xml ├── postcss.config.js ├── public ├── .htaccess ├── favicon.ico ├── fonts │ ├── Gilroy-Black.ttf │ ├── Gilroy-Black.woff2 │ ├── Gilroy-Bold.ttf │ ├── Gilroy-Bold.woff2 │ ├── Gilroy-Medium.ttf │ ├── Gilroy-Medium.woff2 │ ├── Gilroy-Regular.ttf │ ├── Gilroy-Regular.woff2 │ ├── Gilroy-SemiBold.ttf │ └── Gilroy-SemiBold.woff2 ├── images │ ├── checking.svg │ ├── cookies.png │ ├── done.svg │ ├── icons │ │ ├── github-mark.svg │ │ ├── telegram.svg │ │ └── youtube.svg │ ├── logo-dark.svg │ ├── logo-icon.svg │ ├── logo.svg │ ├── progress.svg │ ├── projects │ │ ├── development.jpg │ │ ├── livewire.jpg │ │ ├── moonshine.jpg │ │ ├── solid.jpg │ │ ├── telegram.jpg │ │ └── youtube.jpg │ ├── template.jpg │ ├── time-forward.svg │ └── watermark.svg ├── index.php ├── robots.txt ├── site.webmanifest └── vendor │ ├── moonshine-advanced │ ├── app.js │ ├── css │ │ └── main.css │ ├── js │ │ └── app.js │ └── manifest.json │ ├── moonshine-apexcharts │ └── apexcharts.js │ ├── moonshine-tinymce │ ├── icons │ │ └── default │ │ │ └── icons.min.js │ ├── init.js │ ├── langs │ │ ├── README.md │ │ ├── ru.js │ │ └── uk.js │ ├── license.md │ ├── models │ │ └── dom │ │ │ └── model.min.js │ ├── plugins │ │ ├── accordion │ │ │ └── plugin.min.js │ │ ├── advlist │ │ │ └── plugin.min.js │ │ ├── anchor │ │ │ └── plugin.min.js │ │ ├── autolink │ │ │ └── plugin.min.js │ │ ├── autoresize │ │ │ └── plugin.min.js │ │ ├── autosave │ │ │ └── plugin.min.js │ │ ├── charmap │ │ │ └── plugin.min.js │ │ ├── code │ │ │ └── plugin.min.js │ │ ├── codesample │ │ │ └── plugin.min.js │ │ ├── directionality │ │ │ └── plugin.min.js │ │ ├── emoticons │ │ │ ├── js │ │ │ │ ├── emojiimages.js │ │ │ │ ├── emojiimages.min.js │ │ │ │ ├── emojis.js │ │ │ │ └── emojis.min.js │ │ │ └── plugin.min.js │ │ ├── fullscreen │ │ │ └── plugin.min.js │ │ ├── help │ │ │ ├── js │ │ │ │ └── i18n │ │ │ │ │ └── keynav │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── bg_BG.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── eu.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fr_FR.js │ │ │ │ │ ├── he_IL.js │ │ │ │ │ ├── hi.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu_HU.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── kk.js │ │ │ │ │ ├── ko_KR.js │ │ │ │ │ ├── ms.js │ │ │ │ │ ├── nb_NO.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt_BR.js │ │ │ │ │ ├── pt_PT.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl_SI.js │ │ │ │ │ ├── sv_SE.js │ │ │ │ │ ├── th_TH.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── zh_CN.js │ │ │ │ │ └── zh_TW.js │ │ │ └── plugin.min.js │ │ ├── image │ │ │ └── plugin.min.js │ │ ├── importcss │ │ │ └── plugin.min.js │ │ ├── insertdatetime │ │ │ └── plugin.min.js │ │ ├── link │ │ │ └── plugin.min.js │ │ ├── lists │ │ │ └── plugin.min.js │ │ ├── media │ │ │ └── plugin.min.js │ │ ├── nonbreaking │ │ │ └── plugin.min.js │ │ ├── pagebreak │ │ │ └── plugin.min.js │ │ ├── preview │ │ │ └── plugin.min.js │ │ ├── quickbars │ │ │ └── plugin.min.js │ │ ├── save │ │ │ └── plugin.min.js │ │ ├── searchreplace │ │ │ └── plugin.min.js │ │ ├── table │ │ │ └── plugin.min.js │ │ ├── visualblocks │ │ │ └── plugin.min.js │ │ ├── visualchars │ │ │ └── plugin.min.js │ │ └── wordcount │ │ │ └── plugin.min.js │ ├── skins │ │ ├── content │ │ │ ├── dark │ │ │ │ ├── content.js │ │ │ │ └── content.min.css │ │ │ ├── default │ │ │ │ ├── content.js │ │ │ │ └── content.min.css │ │ │ ├── document │ │ │ │ ├── content.js │ │ │ │ └── content.min.css │ │ │ ├── tinymce-5-dark │ │ │ │ ├── content.js │ │ │ │ └── content.min.css │ │ │ ├── tinymce-5 │ │ │ │ ├── content.js │ │ │ │ └── content.min.css │ │ │ └── writer │ │ │ │ ├── content.js │ │ │ │ └── content.min.css │ │ └── ui │ │ │ ├── oxide-dark │ │ │ ├── content.inline.js │ │ │ ├── content.inline.min.css │ │ │ ├── content.js │ │ │ ├── content.min.css │ │ │ ├── skin.js │ │ │ ├── skin.min.css │ │ │ ├── skin.shadowdom.js │ │ │ └── skin.shadowdom.min.css │ │ │ ├── oxide │ │ │ ├── content.inline.js │ │ │ ├── content.inline.min.css │ │ │ ├── content.js │ │ │ ├── content.min.css │ │ │ ├── skin.js │ │ │ ├── skin.min.css │ │ │ ├── skin.shadowdom.js │ │ │ └── skin.shadowdom.min.css │ │ │ ├── tinymce-5-dark │ │ │ ├── skin.shadowdom.js │ │ │ └── skin.shadowdom.min.css │ │ │ └── tinymce-5 │ │ │ ├── skin.shadowdom.js │ │ │ └── skin.shadowdom.min.css │ ├── themes │ │ └── silver │ │ │ └── theme.min.js │ ├── tinymce.css │ ├── tinymce.d.ts │ └── tinymce.min.js │ └── moonshine │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── assets │ ├── app.js │ ├── main.css │ └── minimalistic.css │ ├── avatar.jpg │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── fonts │ ├── Gilroy-Black.woff2 │ ├── Gilroy-Bold.woff2 │ ├── Gilroy-Medium.woff2 │ ├── Gilroy-Regular.woff2 │ └── Gilroy-SemiBold.woff2 │ ├── libs │ ├── apexcharts │ │ ├── apexcharts-config.js │ │ └── apexcharts.min.js │ └── easymde │ │ ├── easymde.min.css │ │ ├── easymde.min.js │ │ ├── purify.min.js │ │ └── purify.min.js.map │ ├── logo-small.svg │ ├── logo.svg │ ├── manifest.json │ ├── mstile-150x150.png │ ├── robots.txt │ ├── safari-pinned-tab.svg │ └── site.webmanifest ├── resources ├── css │ └── app.css ├── fonts │ ├── Gilroy-Black.ttf │ ├── Gilroy-Black.woff2 │ ├── Gilroy-Bold.ttf │ ├── Gilroy-Bold.woff2 │ ├── Gilroy-Medium.ttf │ ├── Gilroy-Medium.woff2 │ ├── Gilroy-Regular.ttf │ ├── Gilroy-Regular.woff2 │ ├── Gilroy-SemiBold.ttf │ └── Gilroy-SemiBold.woff2 ├── images │ └── icons │ │ ├── checking.svg │ │ ├── done.svg │ │ ├── play.svg │ │ ├── progress.svg │ │ ├── telegram.svg │ │ ├── time-forward.svg │ │ ├── time.svg │ │ └── youtube.svg ├── js │ ├── app.js │ ├── bootstrap.js │ └── main.js ├── sass │ ├── _components.sass │ ├── _mixins.sass │ └── main.sass └── views │ ├── admin │ └── components │ │ └── demo-version-component.blade.php │ ├── articles │ ├── index.blade.php │ ├── shared │ │ ├── author.blade.php │ │ ├── categories.blade.php │ │ └── item.blade.php │ └── show.blade.php │ ├── components │ ├── button.blade.php │ └── title.blade.php │ ├── dictionaries │ ├── index.blade.php │ ├── shared │ │ └── item.blade.php │ └── show.blade.php │ ├── home.blade.php │ ├── layouts │ ├── app.blade.php │ └── social-auth.blade.php │ ├── moonshine │ └── components │ │ └── example-component.blade.php │ ├── shared │ ├── alert.blade.php │ ├── card.blade.php │ ├── footer.blade.php │ └── header.blade.php │ └── vendor │ └── pagination │ └── tailwind.blade.php ├── routes ├── console.php ├── moonshine.php └── web.php ├── storage ├── app │ ├── .gitignore │ ├── private │ │ └── .gitignore │ └── public │ │ └── .gitignore ├── debugbar │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tailwind.config.js ├── tests ├── Feature │ └── ExampleTest.php ├── TestCase.php └── Unit │ └── ExampleTest.php └── vite.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | #=============DOCKER============= 2 | DOCKER_USER=ivan 3 | 4 | COMPOSE_PROJECT_NAME=moonshine-demo 5 | 6 | APP_PATH_HOST=./ 7 | 8 | APP_PATH=/var/www/app 9 | 10 | #php 11 | APP_WEB_PORT=80 12 | 13 | #mysql 14 | APP_MYSQL_PORT=3306 15 | MYSQL_ROOT_PASS=12345 16 | 17 | #redis 18 | APP_REDIS_PORT=6379 19 | 20 | #vite 21 | VITE_PORT=5173 22 | 23 | #Laravel 24 | APP_NAME=Laravel 25 | APP_ENV=local 26 | APP_KEY=base64:LkTwza9SaKmdzblmj4tFwmrIYnAfjtG/P+ksZz0qm/o= 27 | APP_DEBUG=true 28 | APP_TIMEZONE=UTC 29 | APP_URL="http://localhost:${APP_WEB_PORT}" 30 | DEMO_MODE=false 31 | 32 | APP_LOCALE=en 33 | APP_FALLBACK_LOCALE=en 34 | APP_FAKER_LOCALE=en_US 35 | 36 | APP_MAINTENANCE_DRIVER=file 37 | # APP_MAINTENANCE_STORE=database 38 | 39 | PHP_CLI_SERVER_WORKERS=4 40 | 41 | BCRYPT_ROUNDS=12 42 | 43 | LOG_CHANNEL=stack 44 | LOG_STACK=single 45 | LOG_DEPRECATIONS_CHANNEL=null 46 | LOG_LEVEL=debug 47 | 48 | DB_CONNECTION=mysql 49 | DB_HOST="${COMPOSE_PROJECT_NAME}-db" 50 | DB_PORT=3306 51 | DB_DATABASE=moonshine_demo 52 | DB_USERNAME=moonshine_demo 53 | DB_PASSWORD=12345 54 | 55 | SESSION_DRIVER=file 56 | SESSION_LIFETIME=120 57 | SESSION_ENCRYPT=false 58 | SESSION_PATH=/ 59 | SESSION_DOMAIN=null 60 | 61 | BROADCAST_CONNECTION=log 62 | FILESYSTEM_DISK=public 63 | QUEUE_CONNECTION=sync 64 | 65 | CACHE_STORE=file 66 | CACHE_PREFIX= 67 | 68 | REDIS_CLIENT=phpredis 69 | REDIS_HOST="${COMPOSE_PROJECT_NAME}-redis" 70 | REDIS_PASSWORD=null 71 | REDIS_PORT=6379 72 | 73 | MAIL_MAILER=log 74 | MAIL_HOST=127.0.0.1 75 | MAIL_PORT=2525 76 | MAIL_USERNAME=null 77 | MAIL_PASSWORD=null 78 | MAIL_ENCRYPTION=null 79 | MAIL_FROM_ADDRESS="hello@example.com" 80 | MAIL_FROM_NAME="${APP_NAME}" 81 | 82 | AWS_ACCESS_KEY_ID= 83 | AWS_SECRET_ACCESS_KEY= 84 | AWS_DEFAULT_REGION=us-east-1 85 | AWS_BUCKET= 86 | AWS_USE_PATH_STYLE_ENDPOINT=false 87 | 88 | VITE_APP_NAME="${APP_NAME}" 89 | 90 | GITHUB_CLIENT_ID= 91 | GITHUB_CLIENT_SECRET= 92 | GITHUB_CLIENT_CALLBACK="${APP_URL}/admin/socialite/github/callback" 93 | 94 | MOONSHINE_ROUTE_PREFIX=admin 95 | MOONSHINE_TINYMCE_FILE_MANAGER=laravel-filemanager -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | .styleci.yml export-ignore 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /storage/pail 8 | /vendor 9 | .env 10 | .env.backup 11 | .env.production 12 | .phpactor.json 13 | .phpunit.result.cache 14 | Homestead.json 15 | Homestead.yaml 16 | auth.json 17 | npm-debug.log 18 | yarn-error.log 19 | /.fleet 20 | /.idea 21 | /.vscode 22 | /.zed 23 | docker-compose.override.yml 24 | #docker 25 | /docker/volumes/mysql 26 | /docker/volumes/redis 27 | 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | -include .env 2 | 3 | THIS_FILE := $(lastword $(MAKEFILE_LIST)) 4 | 5 | app := $(COMPOSE_PROJECT_NAME)-php 6 | nginx := $(COMPOSE_PROJECT_NAME)-nginx 7 | mysql := $(COMPOSE_PROJECT_NAME)-mysql 8 | app-npm := npm 9 | path := /var/www/app 10 | 11 | #docker 12 | build: 13 | docker-compose -f docker-compose.yml up --build -d $(c) 14 | @echo "$(APP_URL)/admin" 15 | rebuild: 16 | docker-compose up -d --force-recreate --no-deps --build $(r) 17 | @echo "$(APP_URL)/admin" 18 | rebuild-app: 19 | docker-compose up -d --force-recreate --no-deps --build $(app) 20 | up: 21 | docker-compose -f docker-compose.yml up -d $(c) 22 | @echo "$(APP_URL)/admin" 23 | stop: 24 | docker-compose -f docker-compose.yml stop $(c) 25 | it: 26 | docker exec -it $(to) /bin/bash 27 | it-app: 28 | docker exec -it $(app) /bin/bash 29 | it-nginx: 30 | docker exec -it $(nginx) /bin/bash 31 | it-mysql: 32 | docker exec -it $(mysql) /bin/bash 33 | 34 | #laravel 35 | migrate: 36 | docker exec $(app) php $(path)/artisan migrate 37 | migrate-rollback: 38 | docker exec $(app) php $(path)/artisan migrate:rollback 39 | migrate-fresh: 40 | docker exec $(app) php $(path)/artisan migrate:fresh --seed 41 | migration: 42 | docker exec $(app) php $(path)/artisan make:migration $(m) 43 | 44 | #composer 45 | composer-install: 46 | docker exec $(app) composer install 47 | composer-update: 48 | docker exec $(app) composer update 49 | composer-du: 50 | docker exec $(app) composer du 51 | 52 | #npm 53 | npm-install: 54 | docker-compose run --rm --service-ports $(app-npm) install $(c) 55 | npm-update: 56 | docker-compose run --rm --service-ports $(app-npm) update $(c) 57 | npm-build: 58 | docker-compose run --rm --service-ports $(app-npm) run build $(c) 59 | npm-host: 60 | docker-compose run --rm --service-ports $(app-npm) run dev --host $(c) 61 | 62 | #moonshine 63 | demo-install: 64 | make build 65 | make composer-install 66 | docker exec $(app) php $(path)/artisan key:generate 67 | docker exec $(app) php $(path)/artisan storage:link 68 | make npm-install 69 | make npm-build 70 | make migrate-fresh 71 | 72 | #for contributors 73 | update: git-upstream publish 74 | 75 | git-upstream: 76 | git fetch upstream && git merge upstream/3.0 77 | publish: 78 | docker exec $(app) php $(path)/artisan vendor:publish --tag=laravel-assets --force $(c) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | ### Docker 4 | - Create a .env file and copy the values from .env.example, 5 | - Change DOCKER_USER value, 6 | - Configure the ports and database to work (optional). 7 | 8 | The demo can be run to docker-compose. Use: 9 | ``` 10 | make demo-install 11 | ``` 12 | And go to http://localhost or http://localhost/admin 13 | 14 | **Settings.** 15 | 16 | Before you run "make demo-install", you can configure the ports and database to work. 17 | 18 | In .env.example replace 19 | ``` 20 | DOCKER_USER=laravel #OS user 21 | LOCAL_WEB_PORT=80 22 | LOCAL_MYSQL_PORT=3306 23 | LOCAL_REDIS_PORT=6379 24 | ``` 25 | 26 | The database setup is located in docker/volumes/MySQL/dump 27 | Default: 28 | ``` 29 | DB_DATABASE=moonshine_demo 30 | DB_USERNAME=moonshine_demo 31 | DB_PASSWORD=12345 32 | ``` 33 | 34 | ### Manually 35 | - Add database 36 | - Run composer install 37 | - Add .env and configure 38 | - php artisan key:generate 39 | - php artisan storage:link 40 | - php artisan migrate --seed 41 | - Go to /admin 42 | - Optionally, you can configure github or tinymce file manager -------------------------------------------------------------------------------- /app/Console/Commands/ResetCommand.php: -------------------------------------------------------------------------------- 1 | call('migrate:fresh', [ 15 | '--force' => true, 16 | '--seed' => true 17 | ]); 18 | 19 | File::cleanDirectory(storage_path('app/public')); 20 | 21 | return self::SUCCESS; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Http/Controllers/ArticleController.php: -------------------------------------------------------------------------------- 1 | with(['categories', 'author']) 15 | ->latest() 16 | ->paginate(10); 17 | 18 | return view('articles.index', [ 19 | 'articles' => $articles, 20 | ]); 21 | } 22 | 23 | public function show(Article $article): View\Factory|View\View|Application 24 | { 25 | return view('articles.show', compact('article')); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | Dictionary::query()->paginate(10) 15 | ]); 16 | } 17 | 18 | public function show(Dictionary $dictionary): View\Factory|View\View|Application 19 | { 20 | return view('dictionaries.show', compact('dictionary')); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Models/Article.php: -------------------------------------------------------------------------------- 1 | 'collection', 40 | 'data' => 'collection', 41 | 'active' => 'bool' 42 | ]; 43 | 44 | public function categories(): BelongsToMany 45 | { 46 | return $this->belongsToMany(Category::class); 47 | } 48 | 49 | public function author(): BelongsTo 50 | { 51 | return $this->belongsTo(MoonshineUser::class); 52 | } 53 | 54 | public function comments(): HasMany 55 | { 56 | return $this->hasMany(Comment::class); 57 | } 58 | 59 | public function comment(): HasOne 60 | { 61 | return $this->hasOne(Comment::class)->latestOfMany(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/Models/Category.php: -------------------------------------------------------------------------------- 1 | belongsTo(self::class); 19 | } 20 | 21 | public function articles(): BelongsToMany 22 | { 23 | return $this->belongsToMany(Article::class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Models/Comment.php: -------------------------------------------------------------------------------- 1 | 'collection' 22 | ]; 23 | 24 | public function user(): BelongsTo 25 | { 26 | return $this->belongsTo(User::class); 27 | } 28 | 29 | public function article(): BelongsTo 30 | { 31 | return $this->belongsTo(Article::class); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Models/Dictionary.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | protected $fillable = [ 21 | 'name', 22 | 'email', 23 | 'password', 24 | ]; 25 | 26 | /** 27 | * The attributes that should be hidden for serialization. 28 | * 29 | * @var array 30 | */ 31 | protected $hidden = [ 32 | 'password', 33 | 'remember_token', 34 | ]; 35 | 36 | /** 37 | * The attributes that should be cast. 38 | * 39 | * @var array 40 | */ 41 | protected $casts = [ 42 | 'email_verified_at' => 'datetime', 43 | ]; 44 | } 45 | -------------------------------------------------------------------------------- /app/MoonShine/Actions/ArticleMassActive.php: -------------------------------------------------------------------------------- 1 | whereIn('id', $ids) 19 | ->update(['active' => true]) === 1; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/MoonShine/Components/DemoVersionComponent.php: -------------------------------------------------------------------------------- 1 | customAttributes([ 18 | 'class' => 'authentication-form', 19 | ]) 20 | ->action(route('moonshine.authenticate')) 21 | ->fields([ 22 | Text::make(__('moonshine::ui.login.username'), 'username') 23 | ->required() 24 | ->customAttributes([ 25 | 'autofocus' => true, 26 | 'autocomplete' => 'username', 27 | ]) 28 | ->default('admin@moonshine-laravel.com'), 29 | 30 | Password::make(__('moonshine::ui.login.password'), 'password') 31 | ->required() 32 | ->x('bind:value', '"moonshine"'), 33 | 34 | Switcher::make(__('moonshine::ui.login.remember_me'), 'remember'), 35 | ])->submit(__('moonshine::ui.login.login'), [ 36 | 'class' => 'btn-primary btn-lg w-full', 37 | ]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/MoonShine/Http/Controllers/ArticleController.php: -------------------------------------------------------------------------------- 1 | collect('ids') 21 | ->filter() 22 | ->toArray(); 23 | 24 | $success = $action($ids); 25 | $message = __('moonshine::ui.saved'); 26 | $type = ToastType::SUCCESS; 27 | 28 | if (! $success) { 29 | $message = __('moonshine::ui.saved_error'); 30 | $type = ToastType::ERROR; 31 | } 32 | 33 | if ($request->ajax()) { 34 | return $this->json($message, messageType: $type); 35 | } 36 | 37 | $this->toast($message, $type); 38 | 39 | return back(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/MoonShine/Http/Controllers/ProfileController.php: -------------------------------------------------------------------------------- 1 | json( 16 | __('demo.limit'), 17 | messageType: ToastType::WARNING 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/MoonShine/Http/Requests/ArticleMassActiveFormRequest.php: -------------------------------------------------------------------------------- 1 | 'accepted', 16 | 'ids' => ['required', 'array'], 17 | ]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/MoonShine/Layouts/MoonShineLayout.php: -------------------------------------------------------------------------------- 1 | autoloadMenu(); 46 | } 47 | 48 | public function build(): Layout 49 | { 50 | return Layout::make([ 51 | Html::make([ 52 | $this->getHeadComponent(), 53 | Body::make([ 54 | Wrapper::make([ 55 | $this->getSidebarComponent(), 56 | 57 | Div::make([ 58 | DemoVersionComponent::make(), 59 | 60 | Flash::make(), 61 | 62 | $this->getHeaderComponent(), 63 | 64 | Content::make([ 65 | Components::make( 66 | $this->getPage()->getComponents(), 67 | ), 68 | ]), 69 | 70 | $this->getFooterComponent(), 71 | ])->class('layout-page'), 72 | ]), 73 | ])->class('theme-minimalistic'), 74 | ]) 75 | ->customAttributes([ 76 | 'lang' => $this->getHeadLang(), 77 | ]) 78 | ->withAlpineJs() 79 | ->withThemes(), 80 | ]); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/MoonShine/Pages/Article/ArticleDetailPage.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | final class ArticleDetailPage extends DetailPage 25 | { 26 | protected function fields(): iterable 27 | { 28 | return [ 29 | ID::make(), 30 | 31 | BelongsTo::make('Author', resource: MoonShineUserResource::class), 32 | 33 | Number::make('Comments', 'comments_count'), 34 | 35 | Text::make('Title'), 36 | 37 | RangeSlider::make('Age')->fromTo('age_from', 'age_to'), 38 | 39 | Number::make('Rating') 40 | ->link('https://cutcode.dev', 'CutCode', blank: true) 41 | ->stars(), 42 | 43 | Url::make('Link') 44 | ->link('https://cutcode.dev', 'CutCode', blank: true) 45 | ->customWrapperAttributes(['style' => 'white-space: normal;']) 46 | , 47 | 48 | Color::make('Color'), 49 | 50 | Switcher::make('Active'), 51 | ]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/MoonShine/Pages/Article/ArticleIndexPage.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | final class ArticleIndexPage extends IndexPage 25 | { 26 | protected function fields(): iterable 27 | { 28 | return array_filter([ 29 | ID::make()->sortable(), 30 | 31 | BelongsTo::make('Author', resource: MoonShineUserResource::class), 32 | 33 | Number::make('Comments', 'comments_count'), 34 | 35 | Text::make('Title'), 36 | 37 | $this->getResource()?->isListView() ? 38 | Fieldset::make('Files', [ 39 | Image::make('Thumbnail') 40 | ->disk('public') 41 | ->dir('articles'), 42 | ]) : null, 43 | 44 | RangeSlider::make('Age')->fromTo('age_from', 'age_to'), 45 | 46 | Number::make('Rating') 47 | ->link('https://cutcode.dev', 'CutCode', blank: true) 48 | ->stars(), 49 | 50 | Url::make('Link') 51 | ->link('https://cutcode.dev', 'CutCode', blank: true) 52 | ->customWrapperAttributes(['style' => 'white-space: normal;']) 53 | , 54 | 55 | Color::make('Color'), 56 | 57 | Switcher::make('Active'), 58 | ]); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/MoonShine/Pages/Category/CategoryIndexPage.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class CategoryIndexPage extends IndexPage 15 | { 16 | protected function mainLayer(): array 17 | { 18 | return [ 19 | ...$this->getPageButtons(), 20 | TreeComponent::make($this->getResource()), 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/MoonShine/Pages/Dictionary/DictionaryDetailPage.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class DictionaryDetailPage extends DetailPage 17 | { 18 | protected function fields(): iterable 19 | { 20 | return [ 21 | ID::make(), 22 | Text::make('Title'), 23 | TinyMce::make('Description'), 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/MoonShine/Pages/Dictionary/DictionaryFormPage.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class DictionaryFormPage extends FormPage 22 | { 23 | protected function fields(): iterable 24 | { 25 | return [ 26 | Box::make([ 27 | ID::make(), 28 | Text::make('Title')->required(), 29 | Slug::make('Slug') 30 | ->unique() 31 | ->separator('-') 32 | ->from('title') 33 | ->required(), 34 | TinyMce::make('Description'), 35 | ]), 36 | ]; 37 | } 38 | 39 | public function topLayer(): array 40 | { 41 | return [ 42 | Heading::make('Custom top'), 43 | 44 | ...parent::topLayer() 45 | ]; 46 | } 47 | 48 | protected function bottomLayer(): array 49 | { 50 | return [ 51 | ...parent::bottomLayer(), 52 | ChangeLog::make( 53 | 'Changelog', 54 | $this->getResource(), 55 | userResource: MoonShineUserResource::class 56 | ) 57 | ]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/MoonShine/Pages/Dictionary/DictionaryIndexPage.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class DictionaryIndexPage extends IndexPage 19 | { 20 | protected function fields(): iterable 21 | { 22 | return [ 23 | ID::make()->sortable(), 24 | Text::make('Title')->updateInPopover( 25 | $this->getListComponentName() 26 | ), 27 | Slug::make('Slug'), 28 | TinyMce::make('Description'), 29 | ]; 30 | } 31 | 32 | protected function mainLayer(): array 33 | { 34 | return [ 35 | Heading::make('Title'), 36 | 37 | ...parent::mainLayer() 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/MoonShine/Pages/SettingPage.php: -------------------------------------------------------------------------------- 1 | getResource()) { 25 | throw new MoonShineException('Settings resource not found'); 26 | } 27 | 28 | $item = $this->getResource()->getItem(); 29 | 30 | return [ 31 | FormBuilder::make( 32 | $this->getResource()->getRoute('crud.update', $item->getKey()) 33 | ) 34 | ->async() 35 | ->fields([ 36 | Box::make( 37 | $this->getResource() 38 | ->getFormFields() 39 | ->push( 40 | Hidden::make('_method')->setValue('PUT') 41 | ) 42 | ->toArray() 43 | ) 44 | ]) 45 | ->name('crud') 46 | ->fillCast($item, $this->getResource()->getCaster()) 47 | ->submit(__('moonshine::ui.save'), ['class' => 'btn-primary btn-lg']), 48 | ]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/MoonShine/Resources/CategoryResource.php: -------------------------------------------------------------------------------- 1 | sortable(), 55 | BelongsTo::make('Category')->nullable(), 56 | Text::make('Title')->required(), 57 | ]; 58 | } 59 | 60 | protected function formFields(): iterable 61 | { 62 | return [ 63 | Box::make($this->indexFields()) 64 | ]; 65 | } 66 | 67 | protected function detailFields(): iterable 68 | { 69 | return $this->indexFields(); 70 | } 71 | 72 | /** 73 | * @param Category $item 74 | * 75 | */ 76 | protected function rules(mixed $item): array 77 | { 78 | return [ 79 | 'title' => ['required', 'string', 'min:5'], 80 | ]; 81 | } 82 | 83 | protected function search(): array 84 | { 85 | return [ 86 | 'id', 87 | 'title', 88 | ]; 89 | } 90 | 91 | public function treeKey(): ?string 92 | { 93 | return 'category_id'; 94 | } 95 | 96 | public function sortKey(): string 97 | { 98 | return $this->getSortColumn(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/MoonShine/Resources/CommentResource.php: -------------------------------------------------------------------------------- 1 | sortable(), 30 | BelongsTo::make('Article'), 31 | BelongsTo::make('User'), 32 | Text::make('Text')->required(), 33 | ]; 34 | } 35 | 36 | protected function formFields(): array 37 | { 38 | return [ 39 | Box::make([ 40 | ...$this->indexFields() 41 | ]) 42 | ]; 43 | } 44 | 45 | protected function detailFields(): iterable 46 | { 47 | return [ 48 | ...$this->indexFields() 49 | ]; 50 | } 51 | 52 | /** 53 | * @param Comment $item 54 | */ 55 | protected function rules(mixed $item): array 56 | { 57 | return [ 58 | 'text' => ['required', 'string', 'min:1'], 59 | ]; 60 | } 61 | 62 | protected function search(): array 63 | { 64 | return ['id', 'text']; 65 | } 66 | 67 | public function getBadge(): string 68 | { 69 | return (string) Comment::query()->count(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/MoonShine/Resources/DictionaryResource.php: -------------------------------------------------------------------------------- 1 | ['required', 'string', 'min:1'], 48 | 'slug' => ['required', 'string', 'min:1'], 49 | 'description' => ['required', 'string', 'min:1'], 50 | ]; 51 | } 52 | 53 | protected function search(): array 54 | { 55 | return [ 56 | 'id', 57 | 'title', 58 | ]; 59 | } 60 | 61 | protected function filters(): iterable 62 | { 63 | return [ 64 | Text::make('Title'), 65 | ]; 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /app/MoonShine/Resources/SettingResource.php: -------------------------------------------------------------------------------- 1 | getActivePage()?->breadcrumbs([ 30 | '#' => $this->getTitle(), 31 | ]); 32 | } 33 | 34 | protected function pages(): array 35 | { 36 | return [ 37 | SettingPage::class 38 | ]; 39 | } 40 | 41 | protected function formFields(): iterable 42 | { 43 | return [ 44 | ID::make()->sortable(), 45 | Email::make('Email'), 46 | Phone::make('Phone'), 47 | Text::make('Copyright') 48 | ]; 49 | } 50 | 51 | public function getItemID(): int|string|null 52 | { 53 | return 1; 54 | } 55 | 56 | protected function search(): array 57 | { 58 | return []; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/MoonShine/Resources/UserResource.php: -------------------------------------------------------------------------------- 1 | sortable(), 35 | Text::make('Name'), 36 | Email::make('E-mail', 'email'), 37 | ]; 38 | } 39 | 40 | protected function formFields(): iterable 41 | { 42 | return [ 43 | Grid::make([ 44 | Column::make([ 45 | Box::make('Contact information', [ 46 | ID::make()->sortable(), 47 | Text::make('Name'), 48 | Email::make('E-mail', 'email'), 49 | ]), 50 | 51 | LineBreak::make(), 52 | 53 | Box::make('Change password', [ 54 | Password::make('Password') 55 | ->customAttributes(['autocomplete' => 'new-password']) 56 | , 57 | 58 | PasswordRepeat::make('Password repeat') 59 | ->customAttributes(['autocomplete' => 'confirm-password']) 60 | , 61 | ]), 62 | ]), 63 | ]), 64 | ]; 65 | } 66 | 67 | protected function detailFields(): iterable 68 | { 69 | return [ 70 | ...$this->indexFields() 71 | ]; 72 | } 73 | 74 | /** 75 | * @param User $item 76 | * 77 | */ 78 | protected function rules(mixed $item): array 79 | { 80 | return [ 81 | 'name' => 'required', 82 | 'email' => 'sometimes|bail|required|email|unique:users,email' . ($item->exists ? ",$item->id" : ''), 83 | 'password' => ! $item->exists 84 | ? 'required|min:6|required_with:password_repeat|same:password_repeat' 85 | : 'sometimes|nullable|min:6|required_with:password_repeat|same:password_repeat', 86 | ]; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/MoonShine/Sets/DashboardTableWithForm.php: -------------------------------------------------------------------------------- 1 | asyncMethod($method) 26 | ->name('table-form') 27 | ->fields([ 28 | Flex::make([ 29 | DateRange::make('Date') 30 | ->required() 31 | ->withoutWrapper(), 32 | 33 | ActionButton::make('Apply')->dispatchEvent([ 34 | AlpineJs::event(JsEvent::FORM_SUBMIT, 'table-form') 35 | ]), 36 | ]), 37 | ]) 38 | ->hideSubmit(), 39 | 40 | Divider::make(), 41 | 42 | Div::make([ 43 | $this->table(), 44 | ])->class('async-table'), 45 | ]); 46 | } 47 | 48 | public function table(): TableBuilder 49 | { 50 | return TableBuilder::make() 51 | ->fields([ 52 | Text::make('IP'), 53 | Text::make('Email'), 54 | Text::make('Name'), 55 | Text::make('City'), 56 | ]) 57 | ->simple() 58 | ->items([ 59 | ['ip' => fake()->ipv4(), 'email' => fake()->email(), 'name' => fake()->name(), 'city' => fake()->city()], 60 | ['ip' => fake()->ipv4(), 'email' => fake()->email(), 'name' => fake()->name(), 'city' => fake()->city()], 61 | ['ip' => fake()->ipv4(), 'email' => fake()->email(), 'name' => fake()->name(), 'city' => fake()->city()], 62 | ]); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/Policies/ArticlePolicy.php: -------------------------------------------------------------------------------- 1 | moonshine_user_role_id === 1 ||$user->id === $item->author_id; 22 | } 23 | 24 | public function create(MoonshineUser $user) 25 | { 26 | return true; 27 | } 28 | 29 | public function update(MoonshineUser $user, Article $item) 30 | { 31 | return $user->moonshine_user_role_id === 1 ||$user->id === $item->author_id; 32 | } 33 | 34 | public function delete(MoonshineUser $user, Article $item) 35 | { 36 | return $user->moonshine_user_role_id === 1; 37 | } 38 | 39 | public function restore(MoonshineUser $user, Article $item) 40 | { 41 | return $user->moonshine_user_role_id === 1; 42 | } 43 | 44 | public function forceDelete(MoonshineUser $user, Article $item) 45 | { 46 | return $user->moonshine_user_role_id === 1; 47 | } 48 | 49 | public function massDelete(MoonshineUser $user) 50 | { 51 | return false; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/Policies/CategoryPolicy.php: -------------------------------------------------------------------------------- 1 | moonshine_user_role_id === 1; 37 | } 38 | 39 | public function restore(MoonshineUser $user, Category $item) 40 | { 41 | return $user->moonshine_user_role_id === 1; 42 | } 43 | 44 | public function forceDelete(MoonshineUser $user, Category $item) 45 | { 46 | return $user->moonshine_user_role_id === 1; 47 | } 48 | 49 | public function massDelete(MoonshineUser $user) 50 | { 51 | return $user->moonshine_user_role_id === 1; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/Policies/DictionaryPolicy.php: -------------------------------------------------------------------------------- 1 | moonshine_user_role_id === 1; 37 | } 38 | 39 | public function restore(MoonshineUser $user, Dictionary $item) 40 | { 41 | return $user->moonshine_user_role_id === 1; 42 | } 43 | 44 | public function forceDelete(MoonshineUser $user, Dictionary $item) 45 | { 46 | return $user->moonshine_user_role_id === 1; 47 | } 48 | 49 | public function massDelete(MoonshineUser $user) 50 | { 51 | return $user->moonshine_user_role_id === 1; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/Policies/MoonshineUserPolicy.php: -------------------------------------------------------------------------------- 1 | moonshine_user_role_id === 1; 15 | } 16 | 17 | public function view(MoonShineUser $user, MoonShineUser $moonShineUser): bool 18 | { 19 | return $user->moonshine_user_role_id === 1; 20 | } 21 | 22 | public function create(MoonShineUser $user): bool 23 | { 24 | if(config('app.demo_mode', false)) { 25 | return false; 26 | } 27 | 28 | return $user->moonshine_user_role_id === 1; 29 | } 30 | 31 | public function update(MoonShineUser $user, MoonShineUser $moonShineUser): bool 32 | { 33 | if(config('app.demo_mode', false)) { 34 | return false; 35 | } 36 | 37 | return $user->moonshine_user_role_id === 1; 38 | } 39 | 40 | public function delete(MoonShineUser $user, MoonShineUser $moonShineUser): bool 41 | { 42 | if(config('app.demo_mode', false)) { 43 | return false; 44 | } 45 | 46 | return $user->moonshine_user_role_id === 1; 47 | } 48 | 49 | public function restore(MoonShineUser $user, MoonShineUser $moonShineUser): bool 50 | { 51 | if(config('app.demo_mode', false)) { 52 | return false; 53 | } 54 | 55 | return $user->moonshine_user_role_id === 1; 56 | } 57 | 58 | public function forceDelete(MoonShineUser $user, MoonShineUser $moonShineUser): bool 59 | { 60 | if(config('app.demo_mode', false)) { 61 | return false; 62 | } 63 | 64 | return $user->moonshine_user_role_id === 1; 65 | } 66 | 67 | public function massDelete(MoonShineUser $user): bool 68 | { 69 | if(config('app.demo_mode', false)) { 70 | return false; 71 | } 72 | 73 | return $user->moonshine_user_role_id === 1; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/Policies/MoonshineUserRolePolicy.php: -------------------------------------------------------------------------------- 1 | moonshine_user_role_id === 1; 17 | } 18 | 19 | public function view(MoonShineUser $user, MoonshineUserRole $role): bool 20 | { 21 | return $user->moonshine_user_role_id === 1; 22 | } 23 | 24 | public function create(MoonShineUser $user): bool 25 | { 26 | if(config('app.demo_mode', false)) { 27 | return false; 28 | } 29 | 30 | return $user->moonshine_user_role_id === 1; 31 | } 32 | 33 | public function update(MoonShineUser $user, MoonshineUserRole $role): bool 34 | { 35 | if(config('app.demo_mode', false)) { 36 | return false; 37 | } 38 | 39 | return $user->moonshine_user_role_id === 1; 40 | } 41 | 42 | public function delete(MoonShineUser $user, MoonshineUserRole $role): bool 43 | { 44 | if(config('app.demo_mode', false)) { 45 | return false; 46 | } 47 | 48 | return $user->moonshine_user_role_id === 1; 49 | } 50 | 51 | public function restore(MoonShineUser $user, MoonshineUserRole $role): bool 52 | { 53 | if(config('app.demo_mode', false)) { 54 | return false; 55 | } 56 | 57 | return $user->moonshine_user_role_id === 1; 58 | } 59 | 60 | public function forceDelete(MoonShineUser $user, MoonshineUserRole $role): bool 61 | { 62 | if(config('app.demo_mode', false)) { 63 | return false; 64 | } 65 | 66 | return $user->moonshine_user_role_id === 1; 67 | } 68 | 69 | public function massDelete(MoonShineUser $user): bool 70 | { 71 | if(config('app.demo_mode', false)) { 72 | return false; 73 | } 74 | 75 | return $user->moonshine_user_role_id === 1; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | authEnable(); 31 | 32 | $core->autoload(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | routes(function () { 18 | Route::middleware('moonshine') 19 | ->namespace($this->namespace) 20 | ->group(base_path('routes/moonshine.php')); 21 | }); 22 | } 23 | } -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 10 | web: __DIR__.'/../routes/web.php', 11 | commands: __DIR__.'/../routes/console.php', 12 | health: '/up' 13 | ) 14 | ->withMiddleware(function (Middleware $middleware) { 15 | // 16 | }) 17 | ->withExceptions(function (Exceptions $exceptions) { 18 | // 19 | })->create(); 20 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DISK', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Filesystem Disks 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Below you may configure as many filesystem disks as necessary, and you 24 | | may even configure multiple disks for the same driver. Examples for 25 | | most supported storage drivers are configured here for reference. 26 | | 27 | | Supported drivers: "local", "ftp", "sftp", "s3" 28 | | 29 | */ 30 | 31 | 'disks' => [ 32 | 33 | 'local' => [ 34 | 'driver' => 'local', 35 | 'root' => storage_path('app/private'), 36 | 'serve' => true, 37 | 'throw' => false, 38 | ], 39 | 40 | 'public' => [ 41 | 'driver' => 'local', 42 | 'root' => storage_path('app/public'), 43 | 'url' => env('APP_URL').'/storage', 44 | 'visibility' => 'public', 45 | 'throw' => false, 46 | ], 47 | 48 | 's3' => [ 49 | 'driver' => 's3', 50 | 'key' => env('AWS_ACCESS_KEY_ID'), 51 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 52 | 'region' => env('AWS_DEFAULT_REGION'), 53 | 'bucket' => env('AWS_BUCKET'), 54 | 'url' => env('AWS_URL'), 55 | 'endpoint' => env('AWS_ENDPOINT'), 56 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 57 | 'throw' => false, 58 | ], 59 | 60 | ], 61 | 62 | /* 63 | |-------------------------------------------------------------------------- 64 | | Symbolic Links 65 | |-------------------------------------------------------------------------- 66 | | 67 | | Here you may configure the symbolic links that will be created when the 68 | | `storage:link` Artisan command is executed. The array keys should be 69 | | the locations of the links and the values should be their targets. 70 | | 71 | */ 72 | 73 | 'links' => [ 74 | public_path('storage') => storage_path('app/public'), 75 | ], 76 | 77 | ]; 78 | -------------------------------------------------------------------------------- /config/moonshine.php: -------------------------------------------------------------------------------- 1 | env('MOONSHINE_TITLE', 'MoonShine'), 24 | 25 | // Default flags 26 | 'use_migrations' => true, 27 | 'use_notifications' => true, 28 | 'use_database_notifications' => true, 29 | 30 | // Routing 31 | 'domain' => env('MOONSHINE_DOMAIN'), 32 | 'prefix' => env('MOONSHINE_ROUTE_PREFIX', 'admin'), 33 | 'page_prefix' => env('MOONSHINE_PAGE_PREFIX', 'page'), 34 | 'resource_prefix' => env('MOONSHINE_RESOURCE_PREFIX', 'resource'), 35 | 'home_route' => 'moonshine.index', 36 | 37 | // Error handling 38 | 'not_found_exception' => MoonShineNotFoundException::class, 39 | 40 | // Middleware 41 | 'middleware' => [ 42 | EncryptCookies::class, 43 | AddQueuedCookiesToResponse::class, 44 | StartSession::class, 45 | AuthenticateSession::class, 46 | ShareErrorsFromSession::class, 47 | VerifyCsrfToken::class, 48 | SubstituteBindings::class, 49 | ChangeLocale::class, 50 | ], 51 | 52 | // Storage 53 | 'disk' => 'public', 54 | 'disk_options' => [], 55 | 'cache' => 'file', 56 | 57 | // Authentication and profile 58 | 'auth' => [ 59 | 'enabled' => true, 60 | 'guard' => 'moonshine', 61 | 'model' => MoonshineUser::class, 62 | 'middleware' => Authenticate::class, 63 | 'pipelines' => [], 64 | ], 65 | 66 | // Authentication and profile 67 | 'user_fields' => [ 68 | 'username' => 'email', 69 | 'password' => 'password', 70 | 'name' => 'name', 71 | 'avatar' => 'avatar', 72 | ], 73 | 74 | // Layout, pages, forms 75 | 'layout' => \App\MoonShine\Layouts\MoonShineLayout::class, 76 | 77 | 'forms' => [ 78 | 'login' => LoginForm::class, 79 | 'filters' => FiltersForm::class, 80 | ], 81 | 82 | 'pages' => [ 83 | 'dashboard' => Dashboard::class, 84 | 'profile' => ProfilePage::class, 85 | 'login' => LoginPage::class, 86 | 'error' => ErrorPage::class, 87 | ], 88 | 89 | // Localizations 90 | 'locale' => 'en', 91 | 'locales' => [ 92 | 'en', 93 | 'ru', 94 | ], 95 | ]; 96 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'token' => env('POSTMARK_TOKEN'), 19 | ], 20 | 21 | 'ses' => [ 22 | 'key' => env('AWS_ACCESS_KEY_ID'), 23 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 24 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 25 | ], 26 | 27 | 'resend' => [ 28 | 'key' => env('RESEND_KEY'), 29 | ], 30 | 31 | 'slack' => [ 32 | 'notifications' => [ 33 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), 34 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), 35 | ], 36 | ], 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /database/factories/ArticleFactory.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ArticleFactory extends Factory 11 | { 12 | public function definition(): array 13 | { 14 | return [ 15 | 'title' => ucfirst($this->faker->words(2, true)), 16 | 'description' => $this->faker->text(), 17 | 'slug' => $this->faker->slug(), 18 | 'rating' => $this->faker->numberBetween(0,5), 19 | 'link' => $this->faker->url(), 20 | 'age_from' => $this->faker->numberBetween(0, 18), 21 | 'age_to' => $this->faker->numberBetween(18, 60), 22 | 'active' => $this->faker->boolean(), 23 | 'color' => $this->faker->hexColor(), 24 | 'files' => [], 25 | 'data' => [], 26 | 'code' => '' 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /database/factories/CategoryFactory.php: -------------------------------------------------------------------------------- 1 | ucfirst($this->faker->word()), 14 | ]; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name(), 15 | 'email' => $this->faker->unique()->safeEmail(), 16 | 'email_verified_at' => now(), 17 | 'password' => Hash::make(12345), // password 18 | 'remember_token' => Str::random(10), 19 | ]; 20 | } 21 | 22 | public function unverified(): UserFactory 23 | { 24 | return $this->state(function (array $attributes) { 25 | return [ 26 | 'email_verified_at' => null, 27 | ]; 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->timestamp('email_verified_at')->nullable(); 21 | $table->string('password'); 22 | $table->rememberToken(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('users'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 18 | $table->string('token'); 19 | $table->timestamp('created_at')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2019_08_19_000000_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('uuid')->unique(); 19 | $table->text('connection'); 20 | $table->text('queue'); 21 | $table->longText('payload'); 22 | $table->longText('exception'); 23 | $table->timestamp('failed_at')->useCurrent(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('failed_jobs'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->morphs('tokenable'); 19 | $table->string('name'); 20 | $table->string('token', 64)->unique(); 21 | $table->text('abilities')->nullable(); 22 | $table->timestamp('last_used_at')->nullable(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('personal_access_tokens'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2022_09_14_083721_create_articles_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | $table->string('title'); 18 | $table->text('description')->nullable(); 19 | $table->text('thumbnail') 20 | ->nullable(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('articles'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /database/migrations/2022_09_14_083733_create_dictionaries_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('title'); 19 | $table->text('description'); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('dictionaries'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /database/migrations/2022_09_14_164307_create_categories_table.php: -------------------------------------------------------------------------------- 1 | id(); 20 | $table->string('title'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('categories'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /database/migrations/2022_10_30_143621_create_article_category_table.php: -------------------------------------------------------------------------------- 1 | id(); 20 | 21 | $table->foreignIdFor(Article::class) 22 | ->constrained() 23 | ->cascadeOnDelete() 24 | ->cascadeOnUpdate(); 25 | 26 | $table->foreignIdFor(Category::class) 27 | ->constrained() 28 | ->cascadeOnDelete() 29 | ->cascadeOnUpdate(); 30 | 31 | $table->timestamps(); 32 | }); 33 | 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function down() 42 | { 43 | Schema::dropIfExists('article_category'); 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /database/migrations/2022_11_08_125431_add_slugs.php: -------------------------------------------------------------------------------- 1 | string('slug') 18 | ->nullable(); 19 | }); 20 | 21 | Schema::table('dictionaries', function (Blueprint $table) { 22 | $table->string('slug') 23 | ->nullable(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | // 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /database/migrations/2022_11_13_103940_add_author_id_column_to_articles.php: -------------------------------------------------------------------------------- 1 | foreignId('author_id') 13 | ->nullable() 14 | ->constrained('moonshine_users') 15 | ->nullOnDelete() 16 | ->cascadeOnUpdate(); 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /database/migrations/2022_11_13_103940_add_seo_columns_to_articles.php: -------------------------------------------------------------------------------- 1 | string('seo_title') 13 | ->nullable(); 14 | 15 | $table->text('seo_description') 16 | ->nullable(); 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /database/migrations/2023_01_06_094236_create_comments_table.php: -------------------------------------------------------------------------------- 1 | id(); 15 | $table->foreignIdFor(Article::class) 16 | ->constrained() 17 | ->cascadeOnDelete() 18 | ->cascadeOnUpdate(); 19 | 20 | $table->foreignIdFor(User::class) 21 | ->constrained() 22 | ->cascadeOnDelete() 23 | ->cascadeOnUpdate(); 24 | 25 | $table->text('text'); 26 | 27 | $table->timestamps(); 28 | }); 29 | } 30 | 31 | public function down() 32 | { 33 | Schema::dropIfExists('comments'); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /database/migrations/2023_01_07_184655_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('queue')->index(); 19 | $table->longText('payload'); 20 | $table->unsignedTinyInteger('attempts'); 21 | $table->unsignedInteger('reserved_at')->nullable(); 22 | $table->unsignedInteger('available_at'); 23 | $table->unsignedInteger('created_at'); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('jobs'); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /database/migrations/2023_03_18_182739_add_new_columns.php: -------------------------------------------------------------------------------- 1 | integer('rating')->default(0); 19 | $table->integer('age_from')->default(0); 20 | $table->integer('age_to')->default(60); 21 | $table->boolean('active')->default(true); 22 | $table->string('link')->nullable(); 23 | $table->string('color')->nullable(); 24 | $table->text('files')->nullable(); 25 | $table->text('code')->nullable(); 26 | $table->json('data')->nullable(); 27 | }); 28 | 29 | Schema::table('categories', static function (Blueprint $table) { 30 | $table->foreignIdFor(Category::class) 31 | ->nullable() 32 | ->constrained() 33 | ->nullOnDelete() 34 | ->cascadeOnUpdate(); 35 | }); 36 | 37 | Schema::table('users', static function (Blueprint $table) { 38 | $table->integer('phone')->nullable(); 39 | }); 40 | } 41 | 42 | /** 43 | * Reverse the migrations. 44 | * 45 | * @return void 46 | */ 47 | public function down() 48 | { 49 | // 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /database/migrations/2023_03_21_153639_add_new_column_to_comments.php: -------------------------------------------------------------------------------- 1 | text('files')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('comments', function (Blueprint $table) { 29 | // 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2023_06_12_101633_create_settings_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('copyright')->nullable(); 19 | $table->string('email')->nullable(); 20 | $table->string('phone')->nullable(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down(): void 31 | { 32 | Schema::dropIfExists('settings'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /database/migrations/2023_06_12_101646_category_sorting.php: -------------------------------------------------------------------------------- 1 | integer('sorting') 18 | ->default(0); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down(): void 28 | { 29 | Schema::dropColumns('categories', ['sorting']); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /database/migrations/2024_11_03_100651_create_notifications_table.php: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 16 | $table->string('type'); 17 | $table->morphs('notifiable'); 18 | $table->text('data'); 19 | $table->timestamp('read_at')->nullable(); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | */ 27 | public function down(): void 28 | { 29 | Schema::dropIfExists('notifications'); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 22 | 'name' => 'Admin', 23 | 'moonshine_user_role_id' => MoonshineUserRole::DEFAULT_ROLE_ID, 24 | 'email' => 'admin@moonshine-laravel.com', 25 | 'password' => bcrypt('moonshine') 26 | ]); 27 | 28 | MoonshineUserRole::query()->create([ 29 | 'id' => 2, 30 | 'name' => 'Author' 31 | ]); 32 | 33 | Article::factory(20)->create(); 34 | Category::factory(10)->create(); 35 | User::factory(10)->create(); 36 | 37 | DB::table('settings')->insert([ 38 | 'id' => 1, 39 | 'email' => fake()->email(), 40 | 'phone' => fake()->e164PhoneNumber(), 41 | 'copyright' => now()->year 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | nginx: 3 | build: 4 | context: . 5 | dockerfile: ./docker/dockerfiles/nginx/Dockerfile 6 | target: dev 7 | container_name: ${COMPOSE_PROJECT_NAME}-nginx 8 | ports: 9 | - "${APP_WEB_PORT}:80" 10 | volumes: 11 | - ./:${APP_PATH} 12 | depends_on: 13 | - php 14 | 15 | php: 16 | build: 17 | args: 18 | user: ${DOCKER_USER} 19 | uid: 1000 20 | target: dev 21 | context: . 22 | dockerfile: ./docker/dockerfiles/php/Dockerfile 23 | target: dev 24 | container_name: ${COMPOSE_PROJECT_NAME}-php 25 | volumes: 26 | - ./:${APP_PATH} 27 | depends_on: 28 | - db 29 | - redis 30 | 31 | db: 32 | container_name: ${COMPOSE_PROJECT_NAME}-db 33 | build: 34 | context: . 35 | dockerfile: ./docker/dockerfiles/mysql/Dockerfile 36 | volumes: 37 | - ./docker/volumes/mysql:/var/lib/mysql 38 | ports: 39 | - "${APP_MYSQL_PORT}:3306" 40 | environment: 41 | - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASS} 42 | 43 | redis: 44 | container_name: ${COMPOSE_PROJECT_NAME}-redis 45 | image: redis:7.0.11-alpine 46 | volumes: 47 | - ./docker/volumes/redis:/data 48 | ports: 49 | - "${APP_REDIS_PORT}:6379" 50 | 51 | npm: 52 | build: 53 | context: . 54 | dockerfile: ./docker/dockerfiles/node/Dockerfile 55 | container_name: ${COMPOSE_PROJECT_NAME}-npm 56 | volumes: 57 | - ./:${APP_PATH}:cached 58 | - ./.env:${APP_PATH}/.env 59 | working_dir: ${APP_PATH} 60 | profiles: ["npm"] 61 | entrypoint: ['npm'] 62 | ports: 63 | - "${VITE_PORT}:${VITE_PORT}" 64 | -------------------------------------------------------------------------------- /docker/dockerfiles/mysql/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql:8.0.33 2 | 3 | COPY ./docker/dockerfiles/mysql/config/create_database.sql /docker-entrypoint-initdb.d/create_database.sql 4 | COPY ./docker/dockerfiles/mysql/config/mysql.cnf /etc/mysql/conf.d/mysql.cnf 5 | -------------------------------------------------------------------------------- /docker/dockerfiles/mysql/config/create_database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS `moonshine_demo`; 2 | CREATE DATABASE IF NOT EXISTS `moonshine_demo_test`; 3 | 4 | CREATE USER IF NOT EXISTS 'moonshine_demo'@'%' IDENTIFIED BY '12345'; 5 | 6 | GRANT ALL PRIVILEGES ON `moonshine_demo`.* TO 'moonshine_demo'@'%'; 7 | GRANT ALL PRIVILEGES ON `moonshine_demo_test`.* TO 'moonshine_demo'@'%'; 8 | 9 | GRANT SELECT ON `information\_schema`.* TO 'moonshine_demo'@'%'; 10 | FLUSH PRIVILEGES; -------------------------------------------------------------------------------- /docker/dockerfiles/mysql/config/mysql.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | slow_query_log=1 3 | slow_query_log_file=/var/log/mysql/slow.log 4 | long_query_time=0.1 5 | sort_buffer_size=2M 6 | -------------------------------------------------------------------------------- /docker/dockerfiles/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.25.4-alpine AS dev 2 | 3 | COPY . /var/www/app 4 | COPY ./docker/dockerfiles/nginx/config/nginx-php.conf /etc/nginx/nginx.conf 5 | 6 | EXPOSE 80 -------------------------------------------------------------------------------- /docker/dockerfiles/nginx/config/nginx-php.conf: -------------------------------------------------------------------------------- 1 | worker_processes auto; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | http { 8 | access_log off; 9 | error_log /var/log/nginx/error.log; 10 | include /etc/nginx/mime.types; 11 | 12 | gzip on; 13 | gzip_comp_level 4; 14 | gzip_types text/css application/javascript image/jpeg image/png; 15 | 16 | server { 17 | listen 80; 18 | server_name localhost; 19 | root /var/www/app/public; 20 | index index.php index.html; 21 | 22 | error_log /var/log/nginx/app-error.log; 23 | 24 | location ~\.php { 25 | try_files $uri =404; 26 | include /etc/nginx/fastcgi.conf; 27 | fastcgi_pass php:9000; 28 | fastcgi_index index.php; 29 | fastcgi_param PATH_INFO $fastcgi_path_info; 30 | } 31 | 32 | location / { 33 | try_files $uri $uri/ /index.php?$query_string; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docker/dockerfiles/node/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:21.7.1-alpine3.18 -------------------------------------------------------------------------------- /docker/dockerfiles/php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.3.4-fpm AS php 2 | 3 | WORKDIR /var/www/app 4 | 5 | ARG user 6 | ARG uid 7 | 8 | RUN apt-get update && apt-get install -y \ 9 | curl \ 10 | libpng-dev \ 11 | libonig-dev \ 12 | libxml2-dev \ 13 | libzip-dev \ 14 | libc6 \ 15 | zip \ 16 | unzip \ 17 | default-mysql-client 18 | 19 | RUN apt-get clean && rm -rf /var/lib/apt/lists/* 20 | 21 | RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip 22 | 23 | RUN pecl install redis 24 | 25 | COPY --from=composer:2.5.8 /usr/bin/composer /usr/bin/composer 26 | 27 | RUN useradd -G www-data,root -u $uid -d /home/$user $user 28 | 29 | RUN mkdir -p /home/$user/.composer && \ 30 | chown -R $user:$user /home/$user 31 | 32 | COPY ./docker/dockerfiles/php/config/php-dev.ini /usr/local/etc/php/conf.d/php.ini 33 | COPY ./docker/dockerfiles/php/config/php-fpm.conf /usr/local/etc/php-fpm.d/www.conf 34 | 35 | FROM php AS dev 36 | COPY . /var/www/app 37 | RUN if [[ -d '/var/www/app/storage' ]] ; then chmod -R 775 ./storage ./bootstrap/cache ; fi 38 | USER $user -------------------------------------------------------------------------------- /docker/dockerfiles/php/config/php-dev.ini: -------------------------------------------------------------------------------- 1 | [opcache] 2 | opcache.enable=1 3 | opcache.memory_consumption=256 4 | opcache.interned_strings_buffer=16 5 | opcache.validate_timestamps=0 6 | opcache.revalidate_freq=10 7 | 8 | extension=redis.so 9 | 10 | log_errors = On 11 | error_log = /dev/stderr 12 | display_errors = On 13 | display_startup_errors = On 14 | error_reporting = E_ALL 15 | 16 | max_execution_time = 60 17 | memory_limit = 512M 18 | -------------------------------------------------------------------------------- /docker/dockerfiles/php/config/php-fpm.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | error_log = /dev/stderr 3 | 4 | [www] 5 | access.log = /dev/stdout 6 | 7 | catch_workers_output = yes 8 | decorate_workers_output = no 9 | 10 | user = www-data 11 | group = www-data 12 | 13 | listen = /run/php/php8.3-fpm.sock 14 | 15 | listen.owner = www-data 16 | listen.group = www-data 17 | 18 | pm = dynamic 19 | 20 | pm.max_children = 28 21 | 22 | pm.start_servers = 7 23 | 24 | pm.min_spare_servers = 7 25 | 26 | pm.max_spare_servers = 21 27 | -------------------------------------------------------------------------------- /docker/volumes/Nginx/logs/error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/docker/volumes/Nginx/logs/error.log -------------------------------------------------------------------------------- /docker/volumes/Nginx/logs/moonshine-demo.error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/docker/volumes/Nginx/logs/moonshine-demo.error.log -------------------------------------------------------------------------------- /lang/en/demo.php: -------------------------------------------------------------------------------- 1 | 'Demo mode limitation', 5 | 'database' => 'The database is reset every 15 minutes and you can`t edit super user and roles' 6 | ]; 7 | -------------------------------------------------------------------------------- /lang/vendor/moonshine/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 17 | 'password' => 'The provided password is incorrect.', 18 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 19 | ]; 20 | -------------------------------------------------------------------------------- /lang/vendor/moonshine/en/pagination.php: -------------------------------------------------------------------------------- 1 | 'Showing', 17 | 'of' => 'of', 18 | 'to' => 'to', 19 | 'results' => 'results', 20 | 'previous' => '« Previous', 21 | 'next' => 'Next »', 22 | 23 | ]; 24 | -------------------------------------------------------------------------------- /lang/vendor/moonshine/en/ui.php: -------------------------------------------------------------------------------- 1 | 'Profile', 5 | 'dashboard' => 'Dashboard', 6 | 'add' => 'Add', 7 | 'create' => 'Create', 8 | 'edit' => 'Edit', 9 | 'show' => 'Show', 10 | 'save' => 'Save', 11 | 'saved' => 'Saved', 12 | 'saved_error' => 'Error', 13 | 'filters' => 'Filters', 14 | 'search' => 'Search', 15 | 'reset' => 'Reset', 16 | 'total' => 'Total', 17 | 'deleted' => 'Deleted', 18 | 'delete' => 'Delete', 19 | 'deleting' => 'Deleting', 20 | 'download' => 'Download', 21 | 'export' => 'Export', 22 | 'import' => 'Import', 23 | 'cancel' => 'Cancel', 24 | 'confirm' => 'Confirm', 25 | 'close' => 'Close', 26 | 'confirm_message' => 'Are you sure?', 27 | 'back' => 'Back', 28 | 'more' => 'More', 29 | 'all_sections' => 'All sections', 30 | 'file' => 'File', 31 | 'loading' => 'Loading ...', 32 | 'notfound' => 'Records not found', 33 | 'collapse_menu' => 'Collapse menu', 34 | '404' => 'Houston we have a problem page not found', 35 | 'copied' => 'Copied!', 36 | 'notifications' => [ 37 | 'title' => 'Notifications', 38 | 'mark_as_read_all' => 'Mark all as read', 39 | 'mark_as_read' => 'Mark as read', 40 | ], 41 | 'login' => [ 42 | 'title' => 'Welcome to :moonshine_title!', 43 | 'description' => 'Please sign-in to your account', 44 | 'authorization' => 'Authorization', 45 | 'remember_me' => 'Remember me', 46 | 'login' => 'Log in', 47 | 'logout' => 'Log out', 48 | 'username' => 'Username', 49 | 'email' => 'E-mail', 50 | 'password' => 'Password', 51 | ], 52 | 'resource' => [ 53 | 'system' => 'System', 54 | 'role' => 'Roles', 55 | 'name' => 'Name', 56 | 'email' => 'E-mail', 57 | 'password' => 'Password', 58 | 'repeat_password' => 'Repeat password', 59 | 'avatar' => 'Avatar', 60 | 'created_at' => 'Created At', 61 | 'admins_title' => 'Admins', 62 | 'role_title' => 'Roles', 63 | 'role_name' => 'Title', 64 | 65 | 'main_information' => 'Main information', 66 | 'change_password' => 'Change password', 67 | 68 | 'queued' => 'Queued', 69 | 70 | 'export' => [ 71 | 'exported' => 'File exported', 72 | ], 73 | 74 | 'import' => [ 75 | 'imported' => 'Imported', 76 | 'file_required' => 'File is required', 77 | 'extension_not_supported' => 'File extension not supported', 78 | ], 79 | ], 80 | 'choices' => [ 81 | 'no_results' => 'No results found', 82 | 'no_choices' => 'No choices to choose from', 83 | 'item_select' => 'Press to select', 84 | 'unique_item' => 'Only unique values can be added', 85 | 'custom_add_item' => 'Only values matching specific conditions can be added', 86 | 'add_item' => 'Press Enter to add ":value"', 87 | 'max_item' => 'Only :count values can be added', 88 | 'remove_item' => 'Remove item', 89 | ], 90 | ]; 91 | -------------------------------------------------------------------------------- /lang/vendor/moonshine/ru/auth.php: -------------------------------------------------------------------------------- 1 | 'Неверное имя пользователя или пароль.', 16 | 'password' => 'Неверный пароль.', 17 | 'throttle' => 'Слишком много попыток входа. Пожалуйста, попробуйте еще раз через :seconds секунд(ы).', 18 | ]; 19 | -------------------------------------------------------------------------------- /lang/vendor/moonshine/ru/pagination.php: -------------------------------------------------------------------------------- 1 | 'Показано от', 16 | 'of' => 'из', 17 | 'to' => 'до', 18 | 'results' => '', 19 | 'previous' => '« Назад', 20 | 'next' => 'Вперёд »', 21 | ]; 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "build": "vite build", 6 | "dev": "vite" 7 | }, 8 | "devDependencies": { 9 | "@tailwindcss/aspect-ratio": "^0.4.0", 10 | "@tailwindcss/forms": "^0.5.2", 11 | "@tailwindcss/line-clamp": "^0.4.2", 12 | "@tailwindcss/typography": "^0.5.2", 13 | "alpinejs": "3.13.2", 14 | "autoprefixer": "^10.4.20", 15 | "axios": "^1.7.4", 16 | "concurrently": "^9.0.1", 17 | "laravel-vite-plugin": "^1.0", 18 | "postcss": "^8.4.47", 19 | "sass": "^1.57.0", 20 | "sass-loader": "^12.6.0", 21 | "tailwindcss": "^3.4.13", 22 | "vite": "^5.0" 23 | }, 24 | "dependencies": { 25 | "@alpinejs/focus": "^3.14.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | tests/Unit 10 | 11 | 12 | tests/Feature 13 | 14 | 15 | 16 | 17 | app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/Gilroy-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-Black.ttf -------------------------------------------------------------------------------- /public/fonts/Gilroy-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-Black.woff2 -------------------------------------------------------------------------------- /public/fonts/Gilroy-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-Bold.ttf -------------------------------------------------------------------------------- /public/fonts/Gilroy-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-Bold.woff2 -------------------------------------------------------------------------------- /public/fonts/Gilroy-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-Medium.ttf -------------------------------------------------------------------------------- /public/fonts/Gilroy-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-Medium.woff2 -------------------------------------------------------------------------------- /public/fonts/Gilroy-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-Regular.ttf -------------------------------------------------------------------------------- /public/fonts/Gilroy-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-Regular.woff2 -------------------------------------------------------------------------------- /public/fonts/Gilroy-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-SemiBold.ttf -------------------------------------------------------------------------------- /public/fonts/Gilroy-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/fonts/Gilroy-SemiBold.woff2 -------------------------------------------------------------------------------- /public/images/checking.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/images/cookies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/images/cookies.png -------------------------------------------------------------------------------- /public/images/done.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/icons/github-mark.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/icons/telegram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/images/icons/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/images/logo-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/images/projects/development.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/images/projects/development.jpg -------------------------------------------------------------------------------- /public/images/projects/livewire.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/images/projects/livewire.jpg -------------------------------------------------------------------------------- /public/images/projects/moonshine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/images/projects/moonshine.jpg -------------------------------------------------------------------------------- /public/images/projects/solid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/images/projects/solid.jpg -------------------------------------------------------------------------------- /public/images/projects/telegram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/images/projects/telegram.jpg -------------------------------------------------------------------------------- /public/images/projects/youtube.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/images/projects/youtube.jpg -------------------------------------------------------------------------------- /public/images/template.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonshine-software/demo-project/39979a5f846809e517de8ca3d28e45482b166c13/public/images/template.jpg -------------------------------------------------------------------------------- /public/images/time-forward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/images/watermark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 18 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CutCode", 3 | "short_name": "CutCode", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/vendor/moonshine-advanced/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources/css/main.css": { 3 | "file": "css/main.css", 4 | "isEntry": true, 5 | "src": "resources/css/main.css" 6 | }, 7 | "resources/js/app.js": { 8 | "file": "js/app.js", 9 | "isEntry": true, 10 | "src": "resources/js/app.js" 11 | } 12 | } -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/init.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("alpine:init",()=>{Alpine.data("tinymce",(t={},a={})=>({options:t,callbacks:a,async init(){await this.$nextTick();const e=new tinymce.Editor(this.$el.getAttribute("id"),this.config(Alpine.store("darkMode").on),tinymce.EditorManager);e.on("blur",()=>tinymce.activeEditor.save()),e.render(),window.addEventListener("darkMode:toggle",()=>tinymce.remove(e))},fileManager:(e,i,n)=>{const l=window.innerWidth||document.documentElement.clientWidth||document.getElementsByTagName("body")[0].clientWidth,o=window.innerHeight||document.documentElement.clientHeight||document.getElementsByTagName("body")[0].clientHeight;console.log(n);const c=(t.path_absolute??"/")+(t.file_manager??"")+"?editor="+n.fieldname+(n.filetype==="image"?"&type=Images":"&type=Files");tinymce.activeEditor.windowManager.openUrl({url:c,title:"File Manager",width:l*.8,height:o*.8,resizable:"yes",close_previous:"no",onMessage:(r,s)=>e(s.content)})},config(e){for(const i in this.callbacks)this.callbacks[i]=new Function("return "+this.callbacks[i])();return Object.assign({path_absolute:"/",file_manager:"",relative_urls:!1,branding:!1,height:50,skin:e?"oxide-dark":"oxide",content_css:e?"dark":"default",file_picker_callback:this.options.file_manager?this.fileManager:null},this.options,this.callbacks)}}))}); 2 | -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/langs/README.md: -------------------------------------------------------------------------------- 1 | This is where language files should be placed. 2 | 3 | Please DO NOT translate these directly, use this service instead: https://crowdin.com/project/tinymce 4 | -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/license.md: -------------------------------------------------------------------------------- 1 | # Software License Agreement 2 | 3 | **TinyMCE** – [](https://github.com/tinymce/tinymce) 4 | Copyright (c) 2024, Ephox Corporation DBA Tiny Technologies, Inc. 5 | 6 | Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). 7 | -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/plugins/anchor/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.2.1 (2024-07-03) 3 | */ 4 | !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),o=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=("allow_html_in_named_anchor",e=>e.options.get("allow_html_in_named_anchor"));const a="a:not([href])",r=e=>!e,i=e=>e.getAttribute("id")||e.getAttribute("name")||"",l=e=>(e=>"a"===e.nodeName.toLowerCase())(e)&&!e.getAttribute("href")&&""!==i(e),s=e=>e.dom.getParent(e.selection.getStart(),a),d=(e,a)=>{const r=s(e);r?((e,t,o)=>{o.removeAttribute("name"),o.id=t,e.addVisual(),e.undoManager.add()})(e,a,r):((e,a)=>{e.undoManager.transact((()=>{n(e)||e.selection.collapse(!0),e.selection.isCollapsed()?e.insertContent(e.dom.createHTML("a",{id:a})):((e=>{const n=e.dom;t(n).walk(e.selection.getRng(),(e=>{o.each(e,(e=>{var t;l(t=e)&&!t.firstChild&&n.remove(e,!1)}))}))})(e),e.formatter.remove("namedAnchor",void 0,void 0,!0),e.formatter.apply("namedAnchor",{value:a}),e.addVisual())}))})(e,a),e.focus()},c=e=>(e=>r(e.attr("href"))&&!r(e.attr("id")||e.attr("name")))(e)&&!e.firstChild,m=e=>t=>{for(let o=0;ot=>{const o=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",o),o(),()=>{e.off("NodeChange",o)}};e.add("anchor",(e=>{(e=>{(0,e.options.register)("allow_html_in_named_anchor",{processor:"boolean",default:!1})})(e),(e=>{e.on("PreInit",(()=>{e.parser.addNodeFilter("a",m("false")),e.serializer.addNodeFilter("a",m(null))}))})(e),(e=>{e.addCommand("mceAnchor",(()=>{(e=>{const t=(e=>{const t=s(e);return t?i(t):""})(e);e.windowManager.open({title:"Anchor",size:"normal",body:{type:"panel",items:[{name:"id",type:"input",label:"ID",placeholder:"example"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{id:t},onSubmit:t=>{((e,t)=>/^[A-Za-z][A-Za-z0-9\-:._]*$/.test(t)?(d(e,t),!0):(e.windowManager.alert("ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores."),!1))(e,t.getData().id)&&t.close()}})})(e)}))})(e),(e=>{const t=()=>e.execCommand("mceAnchor");e.ui.registry.addToggleButton("anchor",{icon:"bookmark",tooltip:"Anchor",onAction:t,onSetup:t=>{const o=e.selection.selectorChangedWithUnbind("a:not([href])",t.setActive).unbind,n=u(e)(t);return()=>{o(),n()}}}),e.ui.registry.addMenuItem("anchor",{icon:"bookmark",text:"Anchor...",onAction:t,onSetup:u(e)})})(e),e.on("PreInit",(()=>{(e=>{e.formatter.register("namedAnchor",{inline:"a",selector:a,remove:"all",split:!0,deep:!0,attributes:{id:"%value"},onmatch:(e,t,o)=>l(e)})})(e)}))}))}(); -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/plugins/autoresize/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.2.1 (2024-07-03) 3 | */ 4 | !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.Env");const o=e=>t=>t.options.get(e),n=o("min_height"),s=o("max_height"),i=o("autoresize_overflow_padding"),r=o("autoresize_bottom_margin"),g=(e,t)=>{const o=e.getBody();o&&(o.style.overflowY=t?"":"hidden",t||(o.scrollTop=0))},l=(e,t,o,n)=>{var s;const i=parseInt(null!==(s=e.getStyle(t,o,n))&&void 0!==s?s:"",10);return isNaN(i)?0:i},a=(e,o,r,c)=>{var d;const u=e.dom,h=e.getDoc();if(!h)return;if((e=>e.plugins.fullscreen&&e.plugins.fullscreen.isFullscreen())(e))return void g(e,!0);const m=h.documentElement,f=c?c():i(e),p=null!==(d=n(e))&&void 0!==d?d:e.getElement().offsetHeight;let y=p;const S=l(u,m,"margin-top",!0),v=l(u,m,"margin-bottom",!0);let C=m.offsetHeight+S+v+f;C<0&&(C=0);const H=e.getContainer().offsetHeight-e.getContentAreaContainer().offsetHeight;C+H>p&&(y=C+H);const b=s(e);b&&y>b?(y=b,g(e,!0)):g(e,!1);const w=o.get();if(w.set&&(e.dom.setStyles(e.getDoc().documentElement,{"min-height":0}),e.dom.setStyles(e.getBody(),{"min-height":"inherit"})),y!==w.totalHeight&&(C-f!==w.contentHeight||!w.set)){const n=y-w.totalHeight;if(u.setStyle(e.getContainer(),"height",y+"px"),o.set({totalHeight:y,contentHeight:C,set:!0}),(e=>{e.dispatch("ResizeEditor")})(e),t.browser.isSafari()&&(t.os.isMacOS()||t.os.isiOS())){const t=e.getWin();t.scrollTo(t.pageXOffset,t.pageYOffset)}e.hasFocus()&&(e=>{if("setcontent"===(null==e?void 0:e.type.toLowerCase())){const t=e;return!0===t.selection||!0===t.paste}return!1})(r)&&e.selection.scrollIntoView(),(t.browser.isSafari()||t.browser.isChromium())&&n<0&&a(e,o,r,c)}};e.add("autoresize",(e=>{if((e=>{const t=e.options.register;t("autoresize_overflow_padding",{processor:"number",default:1}),t("autoresize_bottom_margin",{processor:"number",default:50})})(e),e.options.isSet("resize")||e.options.set("resize",!1),!e.inline){const o=(e=>{let t={totalHeight:0,contentHeight:0,set:!1};return{get:()=>t,set:e=>{t=e}}})();((e,t)=>{e.addCommand("mceAutoResize",(()=>{a(e,t)}))})(e,o),((e,o)=>{const n=()=>r(e);e.on("init",(s=>{const r=i(e),g=e.dom;g.setStyles(e.getDoc().documentElement,{height:"auto"}),t.browser.isEdge()||t.browser.isIE()?g.setStyles(e.getBody(),{paddingLeft:r,paddingRight:r,"min-height":0}):g.setStyles(e.getBody(),{paddingLeft:r,paddingRight:r}),a(e,o,s,n)})),e.on("NodeChange SetContent keyup FullscreenStateChanged ResizeContent",(t=>{a(e,o,t,n)}))})(e,o)}}))}(); -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/plugins/code/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.2.1 (2024-07-03) 3 | */ 4 | !function(){"use strict";tinymce.util.Tools.resolve("tinymce.PluginManager").add("code",(e=>((e=>{e.addCommand("mceCodeEditor",(()=>{(e=>{const o=(e=>e.getContent({source_view:!0}))(e);e.windowManager.open({title:"Source Code",size:"large",body:{type:"panel",items:[{type:"textarea",name:"code"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{code:o},onSubmit:o=>{((e,o)=>{e.focus(),e.undoManager.transact((()=>{e.setContent(o)})),e.selection.setCursorLocation(),e.nodeChanged()})(e,o.getData().code),o.close()}})})(e)}))})(e),(e=>{const o=()=>e.execCommand("mceCodeEditor");e.ui.registry.addButton("code",{icon:"sourcecode",tooltip:"Source code",onAction:o}),e.ui.registry.addMenuItem("code",{icon:"sourcecode",text:"Source code",onAction:o})})(e),{})))}(); -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/plugins/help/js/i18n/keynav/zh_CN.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('tinymce.html-i18n.help-keynav.zh_CN', 2 | '

开始键盘导航

\n' + 3 | '\n' + 4 | '
\n' + 5 | '
使菜单栏处于焦点
\n' + 6 | '
Windows 或 Linux:Alt+F9
\n' + 7 | '
macOS:⌥F9
\n' + 8 | '
使工具栏处于焦点
\n' + 9 | '
Windows 或 Linux:Alt+F10
\n' + 10 | '
macOS:⌥F10
\n' + 11 | '
使页脚处于焦点
\n' + 12 | '
Windows 或 Linux:Alt+F11
\n' + 13 | '
macOS:⌥F11
\n' + 14 | '
使通知处于焦点
\n' + 15 | '
Windows 或 Linux:Alt+F12
\n' + 16 | '
macOS:⌥F12
\n' + 17 | '
使上下文工具栏处于焦点
\n' + 18 | '
Windows、Linux 或 macOS:Ctrl+F9
\n' + 19 | '
\n' + 20 | '\n' + 21 | '

导航将在第一个 UI 项上开始,其中突出显示该项,或者对于页脚元素路径中的第一项,将为其添加下划线。

\n' + 22 | '\n' + 23 | '

在 UI 部分之间导航

\n' + 24 | '\n' + 25 | '

要从一个 UI 部分移至下一个,请按 Tab

\n' + 26 | '\n' + 27 | '

要从一个 UI 部分移至上一个,请按 Shift+Tab

\n' + 28 | '\n' + 29 | '

这些 UI 部分的 Tab 顺序为:

\n' + 30 | '\n' + 31 | '
    \n' + 32 | '
  1. 菜单栏
  2. \n' + 33 | '
  3. 每个工具栏组
  4. \n' + 34 | '
  5. 边栏
  6. \n' + 35 | '
  7. 页脚中的元素路径
  8. \n' + 36 | '
  9. 页脚中的字数切换按钮
  10. \n' + 37 | '
  11. 页脚中的品牌链接
  12. \n' + 38 | '
  13. 页脚中的编辑器调整大小图柄
  14. \n' + 39 | '
\n' + 40 | '\n' + 41 | '

如果不存在某个 UI 部分,则跳过它。

\n' + 42 | '\n' + 43 | '

如果键盘导航焦点在页脚,并且没有可见的边栏,则按 Shift+Tab 将焦点移至第一个工具栏组而非最后一个。

\n' + 44 | '\n' + 45 | '

在 UI 部分内导航

\n' + 46 | '\n' + 47 | '

要从一个 UI 元素移至下一个,请按相应的箭头键。

\n' + 48 | '\n' + 49 | '

箭头键

\n' + 50 | '\n' + 51 | '
    \n' + 52 | '
  • 在菜单栏中的菜单之间移动。
  • \n' + 53 | '
  • 打开菜单中的子菜单。
  • \n' + 54 | '
  • 在工具栏组中的按钮之间移动。
  • \n' + 55 | '
  • 在页脚的元素路径中的各项之间移动。
  • \n' + 56 | '
\n' + 57 | '\n' + 58 | '

箭头键

\n' + 59 | '\n' + 60 | '
    \n' + 61 | '
  • 在菜单中的菜单项之间移动。
  • \n' + 62 | '
  • 在工具栏弹出菜单中的各项之间移动。
  • \n' + 63 | '
\n' + 64 | '\n' + 65 | '

箭头键在具有焦点的 UI 部分内循环。

\n' + 66 | '\n' + 67 | '

要关闭打开的菜单、打开的子菜单或打开的弹出菜单,请按 Esc 键。

\n' + 68 | '\n' + 69 | '

如果当前的焦点在特定 UI 部分的“顶部”,则按 Esc 键还将完全退出键盘导航。

\n' + 70 | '\n' + 71 | '

执行菜单项或工具栏按钮

\n' + 72 | '\n' + 73 | '

当突出显示所需的菜单项或工具栏按钮时,按 ReturnEnter空格以执行该项。

\n' + 74 | '\n' + 75 | '

在非标签页式对话框中导航

\n' + 76 | '\n' + 77 | '

在非标签页式对话框中,当对话框打开时,第一个交互组件获得焦点。

\n' + 78 | '\n' + 79 | '

通过按 TabShift+Tab,在交互对话框组件之间导航。

\n' + 80 | '\n' + 81 | '

在标签页式对话框中导航

\n' + 82 | '\n' + 83 | '

在标签页式对话框中,当对话框打开时,标签页菜单中的第一个按钮获得焦点。

\n' + 84 | '\n' + 85 | '

通过按 TabShift+Tab,在此对话框的交互组件之间导航。

\n' + 86 | '\n' + 87 | '

通过将焦点移至另一对话框标签页的菜单,然后按相应的箭头键以在可用的标签页间循环,从而切换到该对话框标签页。

\n'); -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/plugins/insertdatetime/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.2.1 (2024-07-03) 3 | */ 4 | !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>t.options.get(e),a=t("insertdatetime_dateformat"),n=t("insertdatetime_timeformat"),r=t("insertdatetime_formats"),s=t("insertdatetime_element"),i="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),o="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),l="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),m="January February March April May June July August September October November December".split(" "),c=(e,t)=>{if((e=""+e).length(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=t.replace("%D","%m/%d/%Y")).replace("%r","%I:%M:%S %p")).replace("%Y",""+a.getFullYear())).replace("%y",""+a.getYear())).replace("%m",c(a.getMonth()+1,2))).replace("%d",c(a.getDate(),2))).replace("%H",""+c(a.getHours(),2))).replace("%M",""+c(a.getMinutes(),2))).replace("%S",""+c(a.getSeconds(),2))).replace("%I",""+((a.getHours()+11)%12+1))).replace("%p",a.getHours()<12?"AM":"PM")).replace("%B",""+e.translate(m[a.getMonth()]))).replace("%b",""+e.translate(l[a.getMonth()]))).replace("%A",""+e.translate(o[a.getDay()]))).replace("%a",""+e.translate(i[a.getDay()]))).replace("%%","%"),u=(e,t)=>{if(s(e)){const a=d(e,t);let n;n=/%[HMSIp]/.test(t)?d(e,"%Y-%m-%dT%H:%M"):d(e,"%Y-%m-%d");const r=e.dom.getParent(e.selection.getStart(),"time");r?((e,t,a,n)=>{const r=e.dom.create("time",{datetime:a},n);e.dom.replace(r,t),e.selection.select(r,!0),e.selection.collapse(!1)})(e,r,n,a):e.insertContent('")}else e.insertContent(d(e,t))};var p=tinymce.util.Tools.resolve("tinymce.util.Tools");const g=e=>t=>{const a=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",a),a(),()=>{e.off("NodeChange",a)}};e.add("insertdatetime",(e=>{(e=>{const t=e.options.register;t("insertdatetime_dateformat",{processor:"string",default:e.translate("%Y-%m-%d")}),t("insertdatetime_timeformat",{processor:"string",default:e.translate("%H:%M:%S")}),t("insertdatetime_formats",{processor:"string[]",default:["%H:%M:%S","%Y-%m-%d","%I:%M:%S %p","%D"]}),t("insertdatetime_element",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mceInsertDate",((t,n)=>{u(e,null!=n?n:a(e))})),e.addCommand("mceInsertTime",((t,a)=>{u(e,null!=a?a:n(e))}))})(e),(e=>{const t=r(e),a=(e=>{let t=e;return{get:()=>t,set:e=>{t=e}}})((e=>{const t=r(e);return t.length>0?t[0]:n(e)})(e)),s=t=>e.execCommand("mceInsertDate",!1,t);e.ui.registry.addSplitButton("insertdatetime",{icon:"insert-time",tooltip:"Insert date/time",select:e=>e===a.get(),fetch:a=>{a(p.map(t,(t=>({type:"choiceitem",text:d(e,t),value:t}))))},onAction:e=>{s(a.get())},onItemAction:(e,t)=>{a.set(t),s(t)},onSetup:g(e)});const i=e=>()=>{a.set(e),s(e)};e.ui.registry.addNestedMenuItem("insertdatetime",{icon:"insert-time",text:"Date/time",getSubmenuItems:()=>p.map(t,(t=>({type:"menuitem",text:d(e,t),onAction:i(t)}))),onSetup:g(e)})})(e)}))}(); -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/plugins/nonbreaking/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.2.1 (2024-07-03) 3 | */ 4 | !function(){"use strict";var n=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=n=>e=>typeof e===n,o=e("boolean"),a=e("number"),t=n=>e=>e.options.get(n),i=t("nonbreaking_force_tab"),s=t("nonbreaking_wrap"),r=(n,e)=>{let o="";for(let a=0;a{const o=s(n)||n.plugins.visualchars?`${r(" ",e)}`:r(" ",e);n.undoManager.transact((()=>n.insertContent(o)))};var l=tinymce.util.Tools.resolve("tinymce.util.VK");const u=n=>e=>{const o=()=>{e.setEnabled(n.selection.isEditable())};return n.on("NodeChange",o),o(),()=>{n.off("NodeChange",o)}};n.add("nonbreaking",(n=>{(n=>{const e=n.options.register;e("nonbreaking_force_tab",{processor:n=>o(n)?{value:n?3:0,valid:!0}:a(n)?{value:n,valid:!0}:{valid:!1,message:"Must be a boolean or number."},default:!1}),e("nonbreaking_wrap",{processor:"boolean",default:!0})})(n),(n=>{n.addCommand("mceNonBreaking",(()=>{c(n,1)}))})(n),(n=>{const e=()=>n.execCommand("mceNonBreaking");n.ui.registry.addButton("nonbreaking",{icon:"non-breaking",tooltip:"Nonbreaking space",onAction:e,onSetup:u(n)}),n.ui.registry.addMenuItem("nonbreaking",{icon:"non-breaking",text:"Nonbreaking space",onAction:e,onSetup:u(n)})})(n),(n=>{const e=i(n);e>0&&n.on("keydown",(o=>{if(o.keyCode===l.TAB&&!o.isDefaultPrevented()){if(o.shiftKey)return;o.preventDefault(),o.stopImmediatePropagation(),c(n,e)}}))})(n)}))}(); -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/plugins/pagebreak/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.2.1 (2024-07-03) 3 | */ 4 | !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=tinymce.util.Tools.resolve("tinymce.Env");const t=e=>a=>a.options.get(e),n=t("pagebreak_separator"),o=t("pagebreak_split_block"),r="mce-pagebreak",s=e=>{const t=``;return e?`

${t}

`:t},c=e=>a=>{const t=()=>{a.setEnabled(e.selection.isEditable())};return e.on("NodeChange",t),t(),()=>{e.off("NodeChange",t)}};e.add("pagebreak",(e=>{(e=>{const a=e.options.register;a("pagebreak_separator",{processor:"string",default:"\x3c!-- pagebreak --\x3e"}),a("pagebreak_split_block",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mcePageBreak",(()=>{e.insertContent(s(o(e)))}))})(e),(e=>{const a=()=>e.execCommand("mcePageBreak");e.ui.registry.addButton("pagebreak",{icon:"page-break",tooltip:"Page break",onAction:a,onSetup:c(e)}),e.ui.registry.addMenuItem("pagebreak",{text:"Page break",icon:"page-break",onAction:a,onSetup:c(e)})})(e),(e=>{const a=n(e),t=()=>o(e),c=new RegExp(a.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,(e=>"\\"+e)),"gi");e.on("BeforeSetContent",(e=>{e.content=e.content.replace(c,s(t()))})),e.on("PreInit",(()=>{e.serializer.addNodeFilter("img",(n=>{let o,s,c=n.length;for(;c--;)if(o=n[c],s=o.attr("class"),s&&-1!==s.indexOf(r)){const n=o.parent;if(n&&e.schema.getBlockElements()[n.name]&&t()){n.type=3,n.value=a,n.raw=!0,o.remove();continue}o.type=3,o.value=a,o.raw=!0}}))}))})(e),(e=>{e.on("ResolveName",(a=>{"IMG"===a.target.nodeName&&e.dom.hasClass(a.target,r)&&(a.name="pagebreak")}))})(e)}))}(); -------------------------------------------------------------------------------- /public/vendor/moonshine-tinymce/plugins/preview/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.2.1 (2024-07-03) 3 | */ 4 | !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.Env"),o=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=e=>t=>t.options.get(e),i=n("content_style"),s=n("content_css_cors"),c=n("body_class"),r=n("body_id");e.add("preview",(e=>{(e=>{e.addCommand("mcePreview",(()=>{(e=>{const n=(e=>{var n;let l="";const a=e.dom.encode,d=null!==(n=i(e))&&void 0!==n?n:"";l+='';const m=s(e)?' crossorigin="anonymous"':"";o.each(e.contentCSS,(t=>{l+='"})),d&&(l+='");const y=r(e),u=c(e),v=' 31 | 32 | 33 | -------------------------------------------------------------------------------- /resources/views/shared/alert.blade.php: -------------------------------------------------------------------------------- 1 | @if(session('message')) 2 |
3 |
4 |
5 |
6 |
7 |

8 | {{ session('message') }} 9 |

10 |
11 | 12 |
13 | 19 |
20 |
21 |
22 |
23 |
24 | @endif 25 | 26 | -------------------------------------------------------------------------------- /resources/views/shared/card.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 | {{ $title }} 10 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /resources/views/shared/header.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 12 | 13 |
14 | 19 |
20 | 21 | 22 |
23 | 29 |
30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /resources/views/vendor/pagination/tailwind.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 | 40 | @endif 41 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | everyFifteenMinutes(); 7 | -------------------------------------------------------------------------------- /routes/moonshine.php: -------------------------------------------------------------------------------- 1 | post('/profile', [ProfileController::class, 'store']) 10 | ->name('profile.store'); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | name('home'); 14 | 15 | Route::controller(ArticleController::class) 16 | ->name('articles.') 17 | ->prefix('articles')->group(function () { 18 | Route::get('/', 'index')->name('index'); 19 | Route::get('/{article:slug}', 'show')->name('show'); 20 | }); 21 | 22 | Route::controller(DictionaryController::class) 23 | ->name('dictionaries.') 24 | ->prefix('dictionaries')->group(function () { 25 | Route::get('/', 'index')->name('index'); 26 | Route::get('/{dictionary:slug}', 'show')->name('show'); 27 | }); 28 | 29 | Route::post('mass-active', [\App\MoonShine\Http\Controllers\ArticleController::class, 'massActive']) 30 | ->name('moonshine.articles.mass-active'); 31 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !private/ 3 | !public/ 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /storage/app/private/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | './storage/framework/views/*.php', 4 | './resources/**/*.blade.php', 5 | './resources/**/*.js', 6 | './resources/**/*.vue', 7 | "./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php", 8 | ], 9 | darkMode: 'class', 10 | theme: { 11 | screens: { 12 | 'xs': '375px', 13 | 'sm': '540px', 14 | 'md': '720px', 15 | 'lg': '960px', 16 | 'xl': '1140px', 17 | '2xl': '1550px', 18 | }, 19 | container: { 20 | center: true, 21 | padding: '20px', 22 | }, 23 | fontFamily: { 24 | 'sans': ['Gilroy', 'sans-serif'], 25 | }, 26 | fontSize: { 27 | 'xxs': ['14px', '1.6em'], 28 | 'xs': ['16px', '1.6em'], 29 | 'sm': ['18px', '1.6em'], 30 | 'md': ['20px', '1.45em'], 31 | 'lg': ['26px', '1.3em'], 32 | 'xl': ['36px', '1.3em'], 33 | '2xl': ['64px', '1.1em'], 34 | '3xl': ['96px', '1.1em'], 35 | }, 36 | extend: { 37 | colors: { 38 | white: "#FFF", 39 | purple: "#7843E9", 40 | pink: "#EC4176", 41 | dark: "#222", 42 | gray: "#454545", 43 | darkblue: "#1E1F43", 44 | body: '#BDBECA', 45 | card: '#323359', 46 | }, 47 | 48 | }, 49 | }, 50 | variants: { 51 | extend: {}, 52 | }, 53 | plugins: [ 54 | require('@tailwindcss/forms'), 55 | require('@tailwindcss/typography'), 56 | require('@tailwindcss/aspect-ratio'), 57 | ], 58 | } 59 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 16 | 17 | $response->assertStatus(200); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | laravel({ 7 | input: ['resources/css/app.css', 'resources/js/app.js', 'resources/sass/main.sass'], 8 | refresh: true, 9 | }), 10 | ], 11 | }); 12 | --------------------------------------------------------------------------------