mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-05 18:23:48 +00:00
Merge remote-tracking branch 'origin/master' into light
# Conflicts: # .env.testing # README.md # composer.json # composer.lock # config/language-translations.php # config/vuefilemanager.php # public/chunks/admin.js # public/chunks/database.js # public/chunks/demo.js # public/chunks/files.js # public/chunks/my-shared-items.js # public/chunks/platform.js # public/chunks/recent-uploads.js # public/chunks/settings-storage.js # public/chunks/settings.js # public/chunks/shared.js # public/chunks/shared/browser.js # public/chunks/shared/single-file.js # public/chunks/sign-in.js # public/chunks/status-check.js # public/chunks/trash.js # public/chunks/user-storage.js # public/css/tailwind.css # public/js/main.js # public/mix-manifest.json # resources/js/components/Subscription/UserFixedSubscriptionDetail.vue # resources/js/store/modules/userAuth.js # resources/js/views/Admin.vue # resources/js/views/Admin/PaymentSettings/PaymentSettingsTab/Payments.vue # resources/js/views/Admin/Plans.vue # resources/js/views/Admin/Plans/Create/CreateFixedPlan.vue # resources/js/views/Admin/Settings/AppSettingsTabs/SignInUp.vue # resources/js/views/Demo.vue # resources/js/views/Frontpage/Homepage.vue # resources/js/views/Platform.vue # resources/views/index.blade.php # src/App/Console/Commands/SetupWebsocketEnvironment.php # src/App/Users/Models/UserLimitation.php # src/Domain/RemoteUpload/Controllers/RemoteUploadFileController.php # src/Domain/RemoteUpload/Controllers/UploadFilesRemotelyForUploadRequestController.php # src/Domain/RemoteUpload/Controllers/VisitorRemoteUploadFileController.php # src/Domain/RemoteUpload/Requests/RemoteUploadRequest.php # src/Domain/Settings/Controllers/StoreBroadcastServiceCredentialsController.php # src/Domain/Settings/Controllers/StorePaymentServiceCredentialsController.php # src/Domain/SetupWizard/Controllers/StoreEnvironmentSettingsController.php # src/Domain/UploadRequest/Controllers/UploadFilesForUploadRequestController.php # src/Support/Upgrading/Controllers/UpgradingVersionsController.php
This commit is contained in:
124
README.md
124
README.md
@@ -178,6 +178,130 @@ If you move your VueFileManager application into another domain or subdomain, yo
|
||||
3. Find `SANCTUM_STATEFUL_DOMAINS` variable and write your new domain location without http protocol.
|
||||
4. Remove your cached config file which is located `/bootstrap/cache/config.php`.
|
||||
|
||||
# Broadcasting
|
||||
### About Broadcasting
|
||||
Broadcasting is responsible for real time app experience. If broadcasting is set, you will be able to get just in time updates in your app.
|
||||
|
||||
For example, remote upload works on the background, while the files are downloading and showing in your view immediately after they are ready without refreshing the app. Or, you will get immediately notifications about new events like team invitations, file requests and many more.
|
||||
|
||||
More real time features will be implemented in incoming updates.
|
||||
|
||||
VueFileManager support 2 types of connections:
|
||||
1. [Pusher](https://pusher.com/) - Suitable for all users and all hosting platforms
|
||||
2. VueFileManager Broadcast Server - Free of charge, suitable for experienced users, limited for VPS servers
|
||||
|
||||
## Install Broadcast Server
|
||||
We strongly recommend only for experienced users to set up and running VueFileManager broadcast server. For others, we recommend to use Pusher service to easy set up and host broadcasting service.
|
||||
|
||||
### Server Requirements
|
||||
**For running app make sure you have:**
|
||||
|
||||
- VPS server with SSH access
|
||||
- PHP >= 8.0.2 version (8.1+ recommended)
|
||||
- Nginx
|
||||
- Supervisor
|
||||
- Certbot
|
||||
|
||||
### Installation
|
||||
We assume you have installed and running properly your VPS server.
|
||||
### Websocket Server Set Up
|
||||
Upload VueFileManager into your server, for example path directory `/var/www/sockets` would be great. Next connect to your server with ssh and change your terminal directory into VueFileManager:
|
||||
```
|
||||
cd /var/www/sockets
|
||||
```
|
||||
Run installation command for websocket server. You will be prompted to type host of which you want to allow incoming requests.
|
||||
```
|
||||
php artisan websockets:install
|
||||
```
|
||||
### Domain & Nginx Set Up
|
||||
Create subdomain for your socket host, for example `socket.vuefilemanager.com` and direct this subdomain to your vps where socket server will be running.
|
||||
|
||||
Then, create nginx config for your subdomain:
|
||||
```
|
||||
nano /etc/nginx/sites-available/socket.conf
|
||||
```
|
||||
And paste the template below, just replace subdomain with yours:
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
server_name socket.vuefilemanager.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:6001;
|
||||
proxy_read_timeout 60;
|
||||
proxy_connect_timeout 60;
|
||||
proxy_redirect off;
|
||||
|
||||
# Allow the use of websockets
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
```
|
||||
Enable your created config by this command:
|
||||
```
|
||||
ln -s /etc/nginx/sites-available/socket.conf /etc/nginx/sites-enabled/
|
||||
```
|
||||
Restart your nginx:
|
||||
```
|
||||
systemctl restart nginx
|
||||
```
|
||||
In the last step, install ssl certificate for your previously created subdomain with certbot:
|
||||
```
|
||||
certbot --nginx
|
||||
```
|
||||
### Supervisor Configuration
|
||||
We need supervisor to manage running your websocket server on the background.
|
||||
In `/etc/supervisor/conf.d` (Debian/Ubuntu) or `/etc/supervisord.d/` (Red Hat/CentOS) create a file `websockets.conf`.
|
||||
|
||||
Just edit php path, project path and user when they are different for your vps:
|
||||
```
|
||||
[program:websockets]
|
||||
command=/usr/bin/php /var/www/socket/artisan websockets:serve
|
||||
numprocs=1
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=www-data
|
||||
```
|
||||
Run command below to start your websocket server:
|
||||
```
|
||||
supervisorctl start websockets
|
||||
```
|
||||
Run command below to stop your websocket server:
|
||||
```
|
||||
supervisorctl stop websockets
|
||||
```
|
||||
When you update code or server for some reason, you must also update running supervisor:
|
||||
```
|
||||
supervisorctl reload
|
||||
```
|
||||
### VueFileManager Set Up
|
||||
Log in to your VueFileManager admin account and go to `Admin / Settings / Environment`.
|
||||
|
||||
Find Broadcasting form and select `VueFileManager` as broadcasting driver. Set your hostname and save the form.
|
||||
|
||||
That's all, you are running your own Broadcast server.
|
||||
|
||||
|
||||
# Subscription Configuration
|
||||
|
||||
## Configuring Production/Testing Environment
|
||||
To set up your subscription, please follow these steps below.
|
||||
1. If you didn't set up your subscription type in Setup Wizard, go to the `Admin / Settings / Application` and find subscription widget. Next set value as `Fixed`.
|
||||
2. Go to the `Admin / Billings` and fill the inputs with your billing information.
|
||||
3. Go to the `Admin / Payments` and turn on the switch `Allow Subscription Payments`.
|
||||
4. Set up credentials for all payment gateway you want. If you set production mode, make sure you fill your credentials with production keys, and vice versa. If needed, don't forget to turn on `live mode` for PayPal.
|
||||
5. Set up your webhooks, you can find your webhook url in payment gateway widget.
|
||||
6. Go to the `Admin / Plans` and create your first plan. Make sure all payment gateways support the currency you want, especially for Paystack, it supports only `GHS, NGN, USD and ZAR`.
|
||||
|
||||
## Upgrading From Testing Environment to the Production Mode
|
||||
1. Go to the `Admin / Payments` and set up credentials for all payment gateway you want with production keys type. Don't forget to turn on `live mode` for PayPal.
|
||||
2. Go to the `Admin / Plans` and delete all your previously created plans. They will be archived.
|
||||
3. Create new production plans you want offer.
|
||||
|
||||
# Developers
|
||||
## Running Environment On Your Localhost
|
||||
|
||||
|
||||
19
changelog.md
19
changelog.md
@@ -1,3 +1,22 @@
|
||||
## Version 2.1.3
|
||||
#### Release date: 9. May 2022
|
||||
- Ability to manually synchronize plans in fixed subscription type
|
||||
- Improved sanitization for .env values to prevent crash your app
|
||||
- Improved reCaptcha validation errors
|
||||
- Fixed issue when upload doesn't start after you drag the file into empty view
|
||||
- Fixed issue when homepage flash to sign in screen after the homepage was disabled in admin panel
|
||||
- Fixed trash navigator issue
|
||||
- Fixed issue when you create plan with 0 team members amount
|
||||
|
||||
## Version 2.1.2
|
||||
#### Release date: 8. May 2022
|
||||
- Fixed issue with chunk uploads (Critical issue affected since 2.1.1)
|
||||
- Fixed issue with creating plan with unlimited team members
|
||||
|
||||
## Version 2.1.1
|
||||
#### Release date: 29. April 2022
|
||||
- Fixed issue with reading image upload
|
||||
|
||||
## Version 2.1.0
|
||||
#### Release date: 25. April 2022
|
||||
- New remote upload function
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"laravel/ui": "^3.4.2",
|
||||
"league/flysystem-aws-s3-v3": "^3.0.9",
|
||||
"league/flysystem-ftp": "^3.0",
|
||||
"makingcg/subscription": "^1.0.5",
|
||||
"matthewbdaly/laravel-azure-storage": "^2.0",
|
||||
"spatie/data-transfer-object": "^3.7.3",
|
||||
"spatie/laravel-backup": "^8.0.8",
|
||||
|
||||
@@ -39,12 +39,13 @@ return [
|
||||
'visibility' => 'public',
|
||||
],
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('S3_ACCESS_KEY_ID'),
|
||||
'secret' => env('S3_SECRET_ACCESS_KEY'),
|
||||
'region' => env('S3_DEFAULT_REGION'),
|
||||
'bucket' => env('S3_BUCKET'),
|
||||
'endpoint' => env('S3_URL'),
|
||||
'driver' => 's3',
|
||||
'key' => env('S3_ACCESS_KEY_ID'),
|
||||
'secret' => env('S3_SECRET_ACCESS_KEY'),
|
||||
'region' => env('S3_DEFAULT_REGION'),
|
||||
'bucket' => env('S3_BUCKET'),
|
||||
'endpoint' => env('S3_URL'),
|
||||
'visibility' => 'private',
|
||||
],
|
||||
'ftp' => [
|
||||
'driver' => 'ftp',
|
||||
|
||||
@@ -462,7 +462,7 @@ return [
|
||||
'server' => 'Server',
|
||||
'environment' => 'Environment',
|
||||
'storage_upload' => 'Storage & Upload',
|
||||
'allow_recaptcha' => 'Allow ReCaptcha',
|
||||
'allow_recaptcha' => 'Allow ReCaptcha v3',
|
||||
'allow_recaptcha_note' => 'ReCaptcha will be allowed on Registration and Contact Us forms.',
|
||||
'account' => 'Account',
|
||||
'search' => 'Search',
|
||||
|
||||
@@ -45,7 +45,7 @@ return [
|
||||
'facebook' => [
|
||||
'client_id' => env('FACEBOOK_CLIENT_ID'),
|
||||
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
|
||||
'redirect' => env('APP_URL') . '/socialite/facebook/callback',
|
||||
'redirect' => env('APP_URL') . '/socialite/facebook/callback/',
|
||||
],
|
||||
|
||||
'recaptcha' => [
|
||||
|
||||
2
public/chunks/app-sign-in-out.js
vendored
2
public/chunks/app-sign-in-out.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/billing.js
vendored
2
public/chunks/billing.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/homepage.js
vendored
2
public/chunks/homepage.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/payments/settings.js
vendored
2
public/chunks/payments/settings.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/plan-create/fixed.js
vendored
2
public/chunks/plan-create/fixed.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/plans.js
vendored
2
public/chunks/plans.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/request-upload.js
vendored
2
public/chunks/request-upload.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/request.js
vendored
2
public/chunks/request.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/shared-with-me.js
vendored
2
public/chunks/shared-with-me.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/sign-up.js
vendored
2
public/chunks/sign-up.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/team-folders.js
vendored
2
public/chunks/team-folders.js
vendored
File diff suppressed because one or more lines are too long
2
public/chunks/user-subscription.js
vendored
2
public/chunks/user-subscription.js
vendored
File diff suppressed because one or more lines are too long
@@ -118,6 +118,11 @@ export default {
|
||||
this.isLoaded = true
|
||||
})
|
||||
}
|
||||
|
||||
// Go to sign in page if homepage is disabled
|
||||
if (!this.$root.$data.config.allowHomepage && window.location.pathname === '/') {
|
||||
this.$router.push({ name: 'SignIn' })
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.$isWindows()) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
'grid-view-sidebar': itemViewType === 'grid' && isVisibleSidebar,
|
||||
}"
|
||||
class="px-4 lg:h-full lg:w-full lg:overflow-y-auto lg:px-0"
|
||||
@drop.stop.prevent="uploadDroppedItems($event)"
|
||||
@drop.prevent="dragStop($event)"
|
||||
@keydown.delete="deleteItems"
|
||||
@dragover="dragEnter"
|
||||
@dragleave="dragLeave"
|
||||
@@ -62,9 +62,7 @@ export default {
|
||||
this.$store.dispatch('deleteItem')
|
||||
}
|
||||
},
|
||||
uploadDroppedItems(event) {
|
||||
this.$uploadDraggedFiles(event, this.currentFolder.data.id)
|
||||
|
||||
dragStop() {
|
||||
this.isDragging = false
|
||||
},
|
||||
dragEnter() {
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
<template>
|
||||
<transition name="popup">
|
||||
<div class="popup" v-if="processingPopup">
|
||||
<div class="popup-wrapper">
|
||||
<div class="popup-content">
|
||||
<div class="spinner-wrapper">
|
||||
<Spinner />
|
||||
<div
|
||||
v-if="processingPopup"
|
||||
class="popup fixed top-0 left-0 right-0 bottom-0 z-50 grid h-full overflow-y-auto p-10 lg:absolute"
|
||||
>
|
||||
<div
|
||||
class="fixed top-0 bottom-0 left-0 right-0 z-10 m-auto w-full bg-white shadow-xl dark:bg-dark-foreground md:relative md:w-[490px] md:rounded-xl"
|
||||
>
|
||||
<div
|
||||
class="flex h-full -translate-y-7 transform items-center justify-center px-8 text-center md:translate-y-0"
|
||||
>
|
||||
<div>
|
||||
<div class="relative pb-16 pt-10">
|
||||
<Spinner />
|
||||
</div>
|
||||
|
||||
<h1 v-if="processingPopup.title" class="mb-2 text-2xl font-bold">
|
||||
{{ processingPopup.title }}
|
||||
</h1>
|
||||
<p v-if="processingPopup.message" class="mb-4 text-sm">
|
||||
{{ processingPopup.message }}
|
||||
</p>
|
||||
</div>
|
||||
<h1 class="title">{{ processingPopup.title }}</h1>
|
||||
<p class="message">{{ processingPopup.message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -17,10 +31,12 @@
|
||||
<script>
|
||||
import Spinner from '../UI/Others/Spinner'
|
||||
import { mapGetters } from 'vuex'
|
||||
import PopupWrapper from './Components/PopupWrapper'
|
||||
|
||||
export default {
|
||||
name: 'ProcessingPopup',
|
||||
components: {
|
||||
PopupWrapper,
|
||||
Spinner,
|
||||
},
|
||||
computed: {
|
||||
@@ -28,107 +44,3 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../../../sass/vuefilemanager/variables';
|
||||
@import '../../../sass/vuefilemanager/mixins';
|
||||
|
||||
.spinner-wrapper {
|
||||
padding-bottom: 90px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.popup {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 20;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.popup-wrapper {
|
||||
z-index: 12;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-width: 480px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) scale(1);
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
box-shadow: $light_mode_popup_shadow;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
.title {
|
||||
@include font-size(22);
|
||||
font-weight: 700;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.message {
|
||||
@include font-size(16);
|
||||
color: #333;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 690px) {
|
||||
.popup-wrapper {
|
||||
padding: 20px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
.title {
|
||||
@include font-size(19);
|
||||
}
|
||||
|
||||
.message {
|
||||
@include font-size(15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.popup-wrapper {
|
||||
background: $dark_mode_foreground;
|
||||
}
|
||||
.popup-content {
|
||||
.title {
|
||||
color: $dark_mode_text_primary;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: $dark_mode_text_secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Animations
|
||||
.popup-enter-active {
|
||||
animation: popup-in 0.35s 0.15s ease both;
|
||||
}
|
||||
|
||||
.popup-leave-active {
|
||||
animation: popup-in 0.15s ease reverse;
|
||||
}
|
||||
|
||||
@keyframes popup-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.7);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
'bg-light-background dark:bg-dark-foreground': isClicked && canHover,
|
||||
'dark:hover:bg-dark-foreground lg:hover:bg-light-background': canHover,
|
||||
}"
|
||||
class="relative z-0 flex h-48 select-none flex-wrap items-center justify-center rounded-lg border-2 border-dashed border-transparent px-1 pt-2 text-center sm:h-56 lg:h-60"
|
||||
class="relative z-0 flex h-48 select-none flex-wrap items-center justify-center rounded-lg border-2 border-dashed border-transparent px-1 pt-2 text-center sm:h-56 lg:h-60 cursor-pointer"
|
||||
:draggable="canDrag"
|
||||
spellcheck="false"
|
||||
>
|
||||
@@ -68,7 +68,7 @@
|
||||
<!--Item Title-->
|
||||
<span
|
||||
class="inline-block w-full overflow-hidden text-ellipsis whitespace-nowrap text-sm font-bold leading-3 tracking-tight md:px-6"
|
||||
:class="{ 'hover:underline': canEditName }"
|
||||
:class="{ 'hover:underline cursor-text': canEditName }"
|
||||
ref="name"
|
||||
@input="renameItem"
|
||||
@keydown.delete.stop
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
'bg-light-background dark:bg-dark-foreground': isClicked && highlight,
|
||||
'hover:bg-light-background dark:hover:bg-dark-foreground': highlight,
|
||||
}"
|
||||
class="flex select-none items-center rounded-xl border-2 border-dashed border-transparent px-2.5 py-2"
|
||||
class="flex select-none items-center rounded-xl border-2 border-dashed border-transparent px-2.5 py-2 cursor-pointer"
|
||||
:draggable="canDrag"
|
||||
spellcheck="false"
|
||||
>
|
||||
@@ -46,7 +46,7 @@
|
||||
<!--Item Title-->
|
||||
<span
|
||||
class="mb-0.5 block overflow-hidden text-ellipsis whitespace-nowrap text-sm font-bold"
|
||||
:class="{ 'hover:underline': canEditName }"
|
||||
:class="{ 'hover:underline cursor-text': canEditName }"
|
||||
style="max-width: 240px"
|
||||
ref="name"
|
||||
@input="renameItem"
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
}"
|
||||
class="chart-wrapper"
|
||||
>
|
||||
<!--<DotLabel class="label" :class="{'offset-top': chart.progress < 5}" :color="chart.color" :title="chart.value" />-->
|
||||
|
||||
<!--Only singe line-->
|
||||
<span
|
||||
v-if="data.length === 1"
|
||||
|
||||
10
resources/js/helpers/functionHelpers.js
vendored
10
resources/js/helpers/functionHelpers.js
vendored
@@ -161,7 +161,7 @@ const FunctionHelpers = {
|
||||
})
|
||||
|
||||
// Start uploading if uploading process isn't running
|
||||
if (store.getters.filesInQueueTotal == 0) this.$handleUploading(store.getters.fileQueue[0])
|
||||
if (store.getters.filesInQueueTotal === 0) this.$handleUploading(store.getters.fileQueue[0])
|
||||
|
||||
// Increase total files in upload bar
|
||||
store.commit('INCREASE_FILES_IN_QUEUES_TOTAL', files.length)
|
||||
@@ -169,7 +169,7 @@ const FunctionHelpers = {
|
||||
|
||||
Vue.prototype.$uploadDraggedFiles = async function (event, parent_id) {
|
||||
// Show alert message when upload is disabled
|
||||
if (!store.getters.user.data.meta.restrictions.canUpload) {
|
||||
if (store.getters.user && !store.getters.user.data.meta.restrictions.canUpload) {
|
||||
Vue.prototype.$temporarilyDisabledUpload()
|
||||
|
||||
return
|
||||
@@ -330,6 +330,12 @@ const FunctionHelpers = {
|
||||
}
|
||||
|
||||
Vue.prototype.$goToFileView = function (id) {
|
||||
// If user is located in trash, then automatically after click on the navigator go to the Files view
|
||||
if (this.$router.currentRoute.name === 'Trash') {
|
||||
this.$router.push({ name: 'Files', params: { id: id } })
|
||||
return
|
||||
}
|
||||
|
||||
let locations = {
|
||||
Public: {name: 'Public', params: { token: this.$route.params.token, id: id }},
|
||||
MySharedItems: { name: 'Files', params: { id: id } },
|
||||
|
||||
8
resources/js/routes/routesIndex.js
vendored
8
resources/js/routes/routesIndex.js
vendored
@@ -7,6 +7,14 @@ const routesIndex = [
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Demo',
|
||||
path: '/demo',
|
||||
component: () => import(/* webpackChunkName: "chunks/demo" */ '../views/Demo'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export default routesIndex
|
||||
|
||||
4
resources/js/store/modules/fileFunctions.js
vendored
4
resources/js/store/modules/fileFunctions.js
vendored
@@ -112,7 +112,7 @@ const actions = {
|
||||
})
|
||||
.catch((error) => {
|
||||
events.$emit('alert:open', {
|
||||
title: error.response.data.message,
|
||||
title: error.response.data.message || i18n.t('popup_error.title'),
|
||||
message: i18n.t('popup_error.message'),
|
||||
})
|
||||
})
|
||||
@@ -175,6 +175,8 @@ const actions = {
|
||||
if (response.data.data.id) {
|
||||
commit('PROCESSING_FILE', false)
|
||||
|
||||
commit('INCREASE_FOLDER_ITEM', response.data.data.attributes.parent_id)
|
||||
|
||||
// Remove first file from file queue
|
||||
commit('SHIFT_FROM_FILE_QUEUE')
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AuthContentWrapper ref="auth" class="h-screen">
|
||||
<AuthContentWrapper v-if="isVisible" ref="auth" class="h-screen">
|
||||
<!--Log In by Email-->
|
||||
<AuthContent name="log-in" :visible="true">
|
||||
<Headline :title="$t('welcome_back')" :description="$t('page_login.subtitle')" />
|
||||
@@ -245,6 +245,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVisible: false,
|
||||
isLoading: false,
|
||||
validSignIn: false,
|
||||
checkedAccount: undefined,
|
||||
@@ -432,14 +433,20 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$scrollTop()
|
||||
this.$store.commit('PROCESSING_POPUP', undefined)
|
||||
|
||||
mounted() {
|
||||
// Redirect if user is authenticated
|
||||
if (this.$root.$data.config.isAuthenticated) {
|
||||
this.$router.push({name: 'Files'})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Show the page when user is not authenticated
|
||||
if (! this.$root.$data.config.isAuthenticated) {
|
||||
this.isVisible = true
|
||||
}
|
||||
|
||||
this.$scrollTop()
|
||||
this.$store.commit('PROCESSING_POPUP', undefined)
|
||||
|
||||
if (this.config.isDemo || this.config.isDev) {
|
||||
this.loginEmail = 'howdy@hi5ve.digital'
|
||||
|
||||
@@ -252,6 +252,12 @@ export default {
|
||||
'Your New Password': error.response.data.errors['password'],
|
||||
})
|
||||
}
|
||||
|
||||
if (error.response.data.errors['reCaptcha']) {
|
||||
events.$emit('alert:open', {
|
||||
title: error.response.data.message,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<MobileToolbar />
|
||||
|
||||
<!--File list & info sidebar-->
|
||||
<div class="flex space-x-3 lg:overflow-hidden grow">
|
||||
<div class="flex space-x-3 lg:overflow-hidden grow" @drop.stop.prevent="uploadDroppedItems($event)" @dragenter.prevent @dragover.prevent>
|
||||
<router-view id="file-view" class="relative w-full" :key="$route.fullPath" />
|
||||
|
||||
<InfoSidebar v-if="isVisibleSidebar" />
|
||||
@@ -91,7 +91,7 @@ export default {
|
||||
DragUI,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isVisibleSidebar', 'config']),
|
||||
...mapGetters(['isVisibleSidebar', 'config', 'currentFolder']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -99,6 +99,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
uploadDroppedItems(event) {
|
||||
this.$uploadDraggedFiles(event, this.currentFolder?.data.id)
|
||||
},
|
||||
contextMenu(event, item) {
|
||||
events.$emit('context-menu:show', event, item)
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<!--<a href="https://codecanyon.net/item/vue-file-manager-with-laravel-backend/25815986">
|
||||
<AlertBox color="rose" class="text-left">
|
||||
<p class="text-rose-500">Please make sure you have only legal copy of VueFileManager <b class="text-rose-500 underline">purchased from CodeCanyon</b>. Any illegal copy can contain malicious software, bugs and others security issues which exposes your files to abuse.</p>
|
||||
<p class="text-rose-500">Please make sure you have only legal copy of VueFileManager <b class="text-rose-500 underline">purchased from CodeCanyon</b>. Any illegal copy can contain malicious software, bugs and others security issues which exposes your files to data breach.</p>
|
||||
</AlertBox>
|
||||
</a>-->
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<MobileToolbar />
|
||||
|
||||
<!--File list & info sidebar-->
|
||||
<div class="flex space-x-3 lg:overflow-hidden grow">
|
||||
<div class="flex space-x-3 lg:overflow-hidden grow" @drop.stop.prevent="uploadDroppedItems($event)" @dragenter.prevent @dragover.prevent>
|
||||
<router-view id="file-view" class="relative w-full" :key="$route.fullPath" />
|
||||
|
||||
<InfoSidebar v-if="isVisibleSidebar" />
|
||||
@@ -81,7 +81,7 @@ export default {
|
||||
Alert,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isVisibleSidebar', 'sharedDetail', 'config']),
|
||||
...mapGetters(['isVisibleSidebar', 'sharedDetail', 'config', 'currentFolder']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -89,6 +89,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
uploadDroppedItems(event) {
|
||||
this.$uploadDraggedFiles(event, this.currentFolder?.data.id)
|
||||
},
|
||||
contextMenu(event, item) {
|
||||
events.$emit('context-menu:show', event, item)
|
||||
},
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<MobileUploadRequestToolBar v-if="canShowUI" />
|
||||
|
||||
<!--File list & info sidebar-->
|
||||
<div class="flex space-x-3 lg:overflow-hidden grow">
|
||||
<div class="flex space-x-3 lg:overflow-hidden grow" @drop.stop.prevent="uploadDroppedItems($event)" @dragenter.prevent @dragover.prevent>
|
||||
<router-view id="file-view" class="relative w-full" :key="$route.fullPath" />
|
||||
|
||||
<InfoSidebarUploadRequest v-if="canShowUI && isVisibleSidebar" />
|
||||
@@ -73,7 +73,7 @@ export default {
|
||||
DragUI,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isVisibleSidebar', 'config', 'uploadRequest', 'fileQueue']),
|
||||
...mapGetters(['isVisibleSidebar', 'config', 'uploadRequest', 'fileQueue', 'currentFolder']),
|
||||
canShowUI() {
|
||||
return (this.uploadRequest && this.uploadRequest.data.attributes.status === 'filling') || this.fileQueue.length > 0
|
||||
}
|
||||
@@ -84,6 +84,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
uploadDroppedItems(event) {
|
||||
this.$uploadDraggedFiles(event, this.currentFolder?.data.id)
|
||||
},
|
||||
contextMenu(event, item) {
|
||||
events.$emit('context-menu:show', event, item)
|
||||
},
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
@php
|
||||
|
||||
try {
|
||||
// Bcmath Extension
|
||||
$storageDefaultSpaceFormatted = isset($settings->default_max_storage_amount)
|
||||
? format_gigabytes($settings->default_max_storage_amount)
|
||||
: format_gigabytes(5);
|
||||
|
||||
$uploadLimit = isset($settings->upload_limit)
|
||||
? format_bytes($settings->upload_limit)
|
||||
: 'undefined';
|
||||
|
||||
$chunkSize = isset($settings->chunk_size)
|
||||
? format_bytes($settings->chunk_size)
|
||||
: format_bytes(64);
|
||||
|
||||
$uploadLimitFormatted = isset($settings->upload_limit)
|
||||
? format_megabytes($settings->upload_limit)
|
||||
: null;
|
||||
|
||||
} catch (MissingExtensionException $exception) {
|
||||
$storageDefaultSpaceFormatted = '5GB';
|
||||
$uploadLimit = 'undefined';
|
||||
$uploadLimitFormatted = 5;
|
||||
$chunkSize = 64000000;
|
||||
}
|
||||
|
||||
try {
|
||||
// User
|
||||
$isUser = auth()->check();
|
||||
@@ -77,11 +102,11 @@
|
||||
|
||||
storageLimit: {{ $settings->storage_limitation ?? 1 }},
|
||||
storageDefaultSpace: {{ $settings->default_max_storage_amount ?? 5 }},
|
||||
storageDefaultSpaceFormatted: '{{ isset($settings->default_max_storage_amount) ? format_gigabytes($settings->default_max_storage_amount) : format_gigabytes(5) }}',
|
||||
storageDefaultSpaceFormatted: '{{ $storageDefaultSpaceFormatted }}',
|
||||
mimetypesBlacklist: '{{ isset($settings->mimetypes_blacklist) ? $settings->mimetypes_blacklist: null}}',
|
||||
uploadLimit: {{ isset($settings->upload_limit) ? format_bytes($settings->upload_limit) : 'undefined' }},
|
||||
uploadLimitFormatted: '{{ isset($settings->upload_limit) ? format_megabytes($settings->upload_limit) : null }}',
|
||||
chunkSize: {{ isset($settings->chunk_size) ? format_bytes($settings->chunk_size) : format_bytes(64) }},
|
||||
uploadLimit: {{ $uploadLimit }},
|
||||
uploadLimitFormatted: '{{ $uploadLimitFormatted }}',
|
||||
chunkSize: {{ $chunkSize }},
|
||||
|
||||
isAuthenticated: {{ $isUser ? 1 : 0 }},
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
namespace Domain\Files\Actions;
|
||||
|
||||
use App\Users\Models\User;
|
||||
use Illuminate\Support\Str;
|
||||
use Domain\Files\Models\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Domain\Files\Requests\UploadRequest;
|
||||
use Domain\Traffic\Actions\RecordUploadAction;
|
||||
use League\Flysystem\UnableToRetrieveMetadata;
|
||||
|
||||
class ProcessFileAction
|
||||
{
|
||||
@@ -26,19 +26,29 @@ class ProcessFileAction
|
||||
public function __invoke(
|
||||
UploadRequest $request,
|
||||
User $user,
|
||||
string $chunkPath,
|
||||
) {
|
||||
string $name,
|
||||
): File {
|
||||
// Get local disk instance
|
||||
$localDisk = Storage::disk('local');
|
||||
|
||||
// Get file data
|
||||
$size = $localDisk->size($chunkPath);
|
||||
$mimetype = $localDisk->mimeType($chunkPath);
|
||||
$name = Str::uuid() . '.' . $request->input('extension');
|
||||
// Get file path
|
||||
$filePath = "files/$user->id/$name";
|
||||
|
||||
// Get upload limit
|
||||
// Get file size
|
||||
$size = $localDisk->size($filePath);
|
||||
|
||||
// Get upload limit size
|
||||
$uploadLimit = get_settings('upload_limit');
|
||||
|
||||
// Get mimetype
|
||||
try {
|
||||
$fileType = getFileType(
|
||||
$localDisk->mimeType($filePath)
|
||||
);
|
||||
} catch (UnableToRetrieveMetadata $e) {
|
||||
$fileType = 'file';
|
||||
}
|
||||
|
||||
// File size handling
|
||||
if ($uploadLimit && $size > format_bytes($uploadLimit)) {
|
||||
abort(413);
|
||||
@@ -47,26 +57,24 @@ class ProcessFileAction
|
||||
// Check if user has enough space to upload file
|
||||
if (! $user->canUpload($size)) {
|
||||
// Delete file from chunk directory
|
||||
$localDisk->delete($chunkPath);
|
||||
|
||||
// Set up response
|
||||
$response = response([
|
||||
'type' => 'error',
|
||||
'message' => __t('user_action_not_allowed'),
|
||||
], 401);
|
||||
$localDisk->delete($filePath);
|
||||
|
||||
// Abort code
|
||||
abort($response);
|
||||
abort(
|
||||
response([
|
||||
'type' => 'error',
|
||||
'message' => __t('user_action_not_allowed'),
|
||||
], 401)
|
||||
);
|
||||
}
|
||||
|
||||
// Move file to user directory
|
||||
$localDisk->move($chunkPath, "files/$user->id/$name");
|
||||
if ($fileType === 'image') {
|
||||
// Create multiple image thumbnails
|
||||
($this->createImageThumbnail)($name, $user->id);
|
||||
|
||||
// Create multiple image thumbnails
|
||||
($this->createImageThumbnail)($name, $user->id);
|
||||
|
||||
// Store exif data if exists
|
||||
$exif = ($this->storeExifData)("files/$user->id/$name");
|
||||
// Store exif data if exists
|
||||
$exif = ($this->storeExifData)($filePath);
|
||||
}
|
||||
|
||||
// Move file to external storage
|
||||
match (config('filesystems.default')) {
|
||||
@@ -78,7 +86,7 @@ class ProcessFileAction
|
||||
// Create new file
|
||||
$file = File::create([
|
||||
'mimetype' => $request->input('extension'),
|
||||
'type' => getFileType($mimetype),
|
||||
'type' => $fileType,
|
||||
'parent_id' => ($this->getFileParentId)($request, $user->id),
|
||||
'name' => $request->input('name'),
|
||||
'basename' => $name,
|
||||
@@ -88,7 +96,10 @@ class ProcessFileAction
|
||||
]);
|
||||
|
||||
// Attach file into the exif data
|
||||
$exif?->update(['file_id' => $file->id]);
|
||||
|
||||
if ($fileType === 'image') {
|
||||
$exif?->update(['file_id' => $file->id]);
|
||||
}
|
||||
|
||||
// Return new file
|
||||
return $file;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
namespace Domain\Files\Actions;
|
||||
|
||||
use Log;
|
||||
use Str;
|
||||
use Storage;
|
||||
use Exception;
|
||||
@@ -31,7 +30,7 @@ class StoreExifDataAction
|
||||
$exif = json_decode(json_encode($exifRaw));
|
||||
|
||||
return Exif::create([
|
||||
'file_id' => Str::uuid(), // TODO: temporary store to prevent crash before app will be successfully upgraded
|
||||
'file_id' => Str::uuid(),
|
||||
'date_time_original' => $exif->DateTimeOriginal ?? null,
|
||||
'artist' => $exif->OwnerName ?? null,
|
||||
'width' => $exif->COMPUTED->Width ?? null,
|
||||
@@ -53,8 +52,6 @@ class StoreExifDataAction
|
||||
'latitude_ref' => $exif->GPSLatitudeRef ?? null,
|
||||
]);
|
||||
} catch (Exception $error) {
|
||||
Log::error('Unable to get exif data');
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Domain\Files\Controllers;
|
||||
|
||||
use Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Domain\Folders\Models\Folder;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Domain\Files\Requests\UploadRequest;
|
||||
@@ -42,8 +44,14 @@ class UploadFileController extends Controller
|
||||
->user
|
||||
: auth()->user();
|
||||
|
||||
// Get file name
|
||||
$name = Str::uuid() . '.' . $request->input('extension');
|
||||
|
||||
// Move file to user directory
|
||||
Storage::disk('local')->move($chunkPath, "files/$user->id/$name");
|
||||
|
||||
// Process file
|
||||
$file = ($this->processFie)($request, $user, $chunkPath);
|
||||
$file = ($this->processFie)($request, $user, $name);
|
||||
|
||||
return response(new FileResource($file), 201);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Domain\Files\Controllers;
|
||||
|
||||
use Str;
|
||||
use Storage;
|
||||
use Domain\Sharing\Models\Share;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Domain\Files\Requests\UploadRequest;
|
||||
@@ -53,8 +55,14 @@ class VisitorUploadFileController extends Controller
|
||||
|
||||
// Proceed after last chunk
|
||||
if ($request->boolean('is_last')) {
|
||||
// Get file name
|
||||
$name = Str::uuid() . '.' . $request->input('extension');
|
||||
|
||||
// Move file to user directory
|
||||
Storage::disk('local')->move($chunkPath, "files/{$shared->user->id}/$name");
|
||||
|
||||
// Process file
|
||||
$file = ($this->processFie)($request, $shared->user, $chunkPath);
|
||||
$file = ($this->processFie)($request, $shared->user, $name);
|
||||
|
||||
// Set public access url
|
||||
$file->setSharedPublicUrl($shared->token);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Domain\Localization\Actions;
|
||||
|
||||
use DB;
|
||||
use Artisan;
|
||||
|
||||
class UpdateLanguageTranslationsAction
|
||||
{
|
||||
@@ -10,9 +11,11 @@ class UpdateLanguageTranslationsAction
|
||||
collect($list)
|
||||
->each(
|
||||
fn (...$item) => DB::table('language_translations')
|
||||
->where('lang', 'en')
|
||||
->where('key', $item[1])
|
||||
->update(['value' => $item[0]])
|
||||
->where('lang', 'en')
|
||||
->where('key', $item[1])
|
||||
->update(['value' => $item[0]])
|
||||
);
|
||||
|
||||
Artisan::call('cache:clear');
|
||||
}
|
||||
}
|
||||
|
||||
61
src/Domain/Settings/Actions/TestPayPalConnectionAction.php
Normal file
61
src/Domain/Settings/Actions/TestPayPalConnectionAction.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace Domain\Settings\Actions;
|
||||
|
||||
use ErrorException;
|
||||
use VueFileManager\Subscription\Support\EngineManager;
|
||||
use VueFileManager\Subscription\Domain\Plans\DTO\CreateFixedPlanData;
|
||||
|
||||
class TestPayPalConnectionAction
|
||||
{
|
||||
public function __construct(
|
||||
public EngineManager $subscription
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke($credentials)
|
||||
{
|
||||
try {
|
||||
// Set temporary stripe connection
|
||||
config([
|
||||
'subscription.credentials.paypal' => [
|
||||
'secret' => $credentials['secret'],
|
||||
'id' => $credentials['key'],
|
||||
'webhook_id' => $credentials['webhook'],
|
||||
'is_live' => $credentials['live'],
|
||||
],
|
||||
]);
|
||||
|
||||
// Define test plan
|
||||
$data = CreateFixedPlanData::fromArray([
|
||||
'type' => 'fixed',
|
||||
'name' => 'Test Plan',
|
||||
'description' => null,
|
||||
'features' => [
|
||||
'max_storage_amount' => 200,
|
||||
'max_team_members' => 20,
|
||||
],
|
||||
'currency' => 'EUR',
|
||||
'amount' => 99,
|
||||
'interval' => 'month',
|
||||
]);
|
||||
|
||||
// Create test plan
|
||||
$plan = $this->subscription
|
||||
->driver('paypal')
|
||||
->createFixedPlan($data);
|
||||
|
||||
// Delete plan
|
||||
$this->subscription
|
||||
->driver('paypal')
|
||||
->deletePlan($plan['id']);
|
||||
} catch (ErrorException $error) {
|
||||
abort(
|
||||
response()->json([
|
||||
'type' => 'service-connection-error',
|
||||
'title' => 'Service Connection Error',
|
||||
'message' => $error->getMessage(),
|
||||
], 401)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/Domain/Settings/Actions/TestPaystackConnectionAction.php
Normal file
59
src/Domain/Settings/Actions/TestPaystackConnectionAction.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace Domain\Settings\Actions;
|
||||
|
||||
use ErrorException;
|
||||
use VueFileManager\Subscription\Support\EngineManager;
|
||||
use VueFileManager\Subscription\Domain\Plans\DTO\CreateFixedPlanData;
|
||||
|
||||
class TestPaystackConnectionAction
|
||||
{
|
||||
public function __construct(
|
||||
public EngineManager $subscription
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke($credentials)
|
||||
{
|
||||
try {
|
||||
// Set temporary paystack connection
|
||||
config([
|
||||
'subscription.credentials.paystack' => [
|
||||
'secret' => $credentials['secret'],
|
||||
'public_key' => $credentials['key'],
|
||||
],
|
||||
]);
|
||||
|
||||
// Define test plan
|
||||
$data = CreateFixedPlanData::fromArray([
|
||||
'type' => 'fixed',
|
||||
'name' => 'Test Plan',
|
||||
'description' => null,
|
||||
'features' => [
|
||||
'max_storage_amount' => 200,
|
||||
'max_team_members' => 20,
|
||||
],
|
||||
'currency' => 'ZAR',
|
||||
'amount' => 99999,
|
||||
'interval' => 'month',
|
||||
]);
|
||||
|
||||
// Create test plan
|
||||
$plan = $this->subscription
|
||||
->driver('paystack')
|
||||
->createFixedPlan($data);
|
||||
|
||||
// Delete plan
|
||||
$this->subscription
|
||||
->driver('paystack')
|
||||
->deletePlan($plan['id']);
|
||||
} catch (ErrorException $error) {
|
||||
abort(
|
||||
response()->json([
|
||||
'type' => 'service-connection-error',
|
||||
'title' => 'Service Connection Error',
|
||||
'message' => $error->getMessage(),
|
||||
], 401)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/Domain/Settings/Actions/TestStripeConnectionAction.php
Normal file
60
src/Domain/Settings/Actions/TestStripeConnectionAction.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
namespace Domain\Settings\Actions;
|
||||
|
||||
use ErrorException;
|
||||
use VueFileManager\Subscription\Support\EngineManager;
|
||||
use VueFileManager\Subscription\Domain\Plans\DTO\CreateFixedPlanData;
|
||||
|
||||
class TestStripeConnectionAction
|
||||
{
|
||||
public function __construct(
|
||||
public EngineManager $subscription
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke($credentials)
|
||||
{
|
||||
try {
|
||||
// Set temporary stripe connection
|
||||
config([
|
||||
'subscription.credentials.stripe' => [
|
||||
'secret' => $credentials['secret'],
|
||||
'public_key' => $credentials['key'],
|
||||
'webhook_key' => $credentials['webhook'],
|
||||
],
|
||||
]);
|
||||
|
||||
// Define test plan
|
||||
$data = CreateFixedPlanData::fromArray([
|
||||
'type' => 'fixed',
|
||||
'name' => 'Test Plan',
|
||||
'description' => null,
|
||||
'features' => [
|
||||
'max_storage_amount' => 200,
|
||||
'max_team_members' => 20,
|
||||
],
|
||||
'currency' => 'EUR',
|
||||
'amount' => 99,
|
||||
'interval' => 'month',
|
||||
]);
|
||||
|
||||
// Create test plan
|
||||
$plan = $this->subscription
|
||||
->driver('stripe')
|
||||
->createFixedPlan($data);
|
||||
|
||||
// Delete plan
|
||||
$this->subscription
|
||||
->driver('stripe')
|
||||
->deletePlan($plan['id']);
|
||||
} catch (ErrorException $error) {
|
||||
abort(
|
||||
response()->json([
|
||||
'type' => 'service-connection-error',
|
||||
'title' => 'Service Connection Error',
|
||||
'message' => $error->getMessage(),
|
||||
], 401)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,9 +202,9 @@ if (! function_exists('setEnvironmentValue')) {
|
||||
if ($keyPosition) {
|
||||
$endOfLinePosition = strpos($str, "\n", $keyPosition);
|
||||
$oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
|
||||
$str = str_replace($oldLine, "{$envKey}={$envValue}", $str);
|
||||
$str = str_replace($oldLine, "{$envKey}=\"{$envValue}\"", $str);
|
||||
} else {
|
||||
$str .= "\n$envKey=$envValue";
|
||||
$str .= "\n$envKey=\"$envValue\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
namespace Tests\Domain\Files;
|
||||
|
||||
use Event;
|
||||
use Storage;
|
||||
use Tests\TestCase;
|
||||
use App\Users\Models\User;
|
||||
@@ -10,8 +9,6 @@ use Domain\Files\Models\File;
|
||||
use Domain\Folders\Models\Folder;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Domain\Settings\Models\Setting;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Domain\RemoteUpload\Events\RemoteFileCreatedEvent;
|
||||
|
||||
class FileTest extends TestCase
|
||||
{
|
||||
|
||||
69
tests/Domain/RemoteUpload/RemoteUploadTest.php
Normal file
69
tests/Domain/RemoteUpload/RemoteUploadTest.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace Tests\Domain\RemoteUpload;
|
||||
|
||||
use Event;
|
||||
use Storage;
|
||||
use Tests\TestCase;
|
||||
use App\Users\Models\User;
|
||||
use Domain\Files\Models\File;
|
||||
use Domain\Folders\Models\Folder;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Domain\RemoteUpload\Events\RemoteFileCreatedEvent;
|
||||
|
||||
class RemoteUploadTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function it_remotely_upload_new_file()
|
||||
{
|
||||
Event::fake([
|
||||
RemoteFileCreatedEvent::class,
|
||||
]);
|
||||
|
||||
$user = User::factory()
|
||||
->hasSettings()
|
||||
->create();
|
||||
|
||||
$folder = Folder::factory()
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
$fakeFile = UploadedFile::fake()
|
||||
->create('top-secret-document.pdf', 12000000, 'application/pdf');
|
||||
|
||||
Http::fake([
|
||||
'https://fake.com/top-secret-document.pdf' => Http::response($fakeFile->getContent()),
|
||||
'https://fake.com/another-secret-document.pdf' => Http::response($fakeFile->getContent()),
|
||||
]);
|
||||
|
||||
$this
|
||||
->actingAs($user)
|
||||
->postJson('/api/upload/remote', [
|
||||
'urls' => [
|
||||
'https://fake.com/top-secret-document.pdf',
|
||||
'https://fake.com/another-secret-document.pdf',
|
||||
],
|
||||
'parent_id' => $folder->id,
|
||||
])->assertStatus(201);
|
||||
|
||||
$this
|
||||
->assertDatabaseHas('files', [
|
||||
'user_id' => $user->id,
|
||||
'name' => 'top-secret-document',
|
||||
'parent_id' => $folder->id,
|
||||
])
|
||||
->assertDatabaseHas('files', [
|
||||
'name' => 'another-secret-document',
|
||||
]);
|
||||
|
||||
File::all()
|
||||
->each(function ($file) {
|
||||
Event::assertDispatched(fn (RemoteFileCreatedEvent $event) => $event->payload['file']->id === $file->id);
|
||||
|
||||
Storage::assertExists("files/$file->user_id/$file->basename");
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user