activestorage_saas 5.2.5.2 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +260 -0
  3. data/CHANGELOG.md +12 -0
  4. data/COVERAGE.md +64 -0
  5. data/Gemfile +8 -4
  6. data/README.md +206 -17
  7. data/lib/active_storage/service/saas_service.rb +15 -10
  8. data/lib/active_storage_saas/blob_model_mixin.rb +48 -0
  9. data/lib/active_storage_saas/direct_uploads_controller_mixin.rb +31 -0
  10. data/lib/active_storage_saas/engine.rb +26 -5
  11. data/lib/active_storage_saas/routes.rb +3 -3
  12. data/lib/active_storage_saas/storage_service_configuration_model_mixin.rb +48 -0
  13. data/lib/active_storage_saas.rb +4 -2
  14. data/lib/generators/active_storage_saas/install/USAGE +5 -0
  15. data/lib/generators/active_storage_saas/install/install_generator.rb +19 -0
  16. data/lib/generators/active_storage_saas/install/templates/config/initializers/active_storage_saas.rb +6 -0
  17. metadata +13 -21
  18. data/.rspec +0 -3
  19. data/.rubocop.yml +0 -17
  20. data/Gemfile.lock +0 -112
  21. data/Rakefile +0 -12
  22. data/activestorage_saas.gemspec +0 -33
  23. data/app/controller/active_storage_saas/direct_uploads_controller.rb +0 -28
  24. data/app/javascript/active_storage_saas/direct_upload_controller/blob_record.js +0 -73
  25. data/app/javascript/active_storage_saas/direct_upload_controller/blob_upload.js +0 -45
  26. data/app/javascript/active_storage_saas/direct_upload_controller/direct_upload.js +0 -48
  27. data/app/javascript/active_storage_saas/direct_upload_controller/file_checksum.js +0 -53
  28. data/app/javascript/active_storage_saas/direct_upload_controller/helpers.js +0 -51
  29. data/app/javascript/active_storage_saas/direct_upload_controller.js +0 -78
  30. data/app/models/tenant_storage_service.rb +0 -5
  31. data/db/migrate/001_activestorage_saas_tables.rb +0 -17
  32. data/lib/active_storage_saas/blob_patch.rb +0 -51
  33. data/sig/activestorage_saas.rbs +0 -4
@@ -1,48 +0,0 @@
1
- import { FileChecksum } from "./file_checksum"
2
- import { BlobRecord } from "./blob_record"
3
- import { BlobUpload } from "./blob_upload"
4
-
5
- let id = 0
6
-
7
- export class DirectUpload {
8
- constructor(file, url, delegate) {
9
- this.id = ++id
10
- this.file = file
11
- this.url = url
12
- this.delegate = delegate
13
- }
14
-
15
- create(callback) {
16
- FileChecksum.create(this.file, (error, checksum) => {
17
- if (error) {
18
- callback(error)
19
- return
20
- }
21
-
22
- const blob = new BlobRecord(this.file, checksum, this.url)
23
- notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr)
24
-
25
- blob.create(error => {
26
- if (error) {
27
- callback(error)
28
- } else {
29
- const upload = new BlobUpload(blob)
30
- notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr)
31
- upload.create(error => {
32
- if (error) {
33
- callback(error)
34
- } else {
35
- callback(null, blob.toJSON())
36
- }
37
- })
38
- }
39
- })
40
- })
41
- }
42
- }
43
-
44
- function notify(object, methodName, ...messages) {
45
- if (object && typeof object[methodName] == "function") {
46
- return object[methodName](...messages)
47
- }
48
- }
@@ -1,53 +0,0 @@
1
- import SparkMD5 from "spark-md5"
2
-
3
- const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
4
-
5
- export class FileChecksum {
6
- static create(file, callback) {
7
- const instance = new FileChecksum(file)
8
- instance.create(callback)
9
- }
10
-
11
- constructor(file) {
12
- this.file = file
13
- this.chunkSize = 2097152 // 2MB
14
- this.chunkCount = Math.ceil(this.file.size / this.chunkSize)
15
- this.chunkIndex = 0
16
- }
17
-
18
- create(callback) {
19
- this.callback = callback
20
- this.md5Buffer = new SparkMD5.ArrayBuffer
21
- this.fileReader = new FileReader
22
- this.fileReader.addEventListener("load", event => this.fileReaderDidLoad(event))
23
- this.fileReader.addEventListener("error", event => this.fileReaderDidError(event))
24
- this.readNextChunk()
25
- }
26
-
27
- fileReaderDidLoad(event) {
28
- this.md5Buffer.append(event.target.result)
29
-
30
- if (!this.readNextChunk()) {
31
- const binaryDigest = this.md5Buffer.end(true)
32
- const base64digest = btoa(binaryDigest)
33
- this.callback(null, base64digest)
34
- }
35
- }
36
-
37
- fileReaderDidError(event) {
38
- this.callback(`Error reading ${this.file.name}`)
39
- }
40
-
41
- readNextChunk() {
42
- if (this.chunkIndex < this.chunkCount || (this.chunkIndex == 0 && this.chunkCount == 0)) {
43
- const start = this.chunkIndex * this.chunkSize
44
- const end = Math.min(start + this.chunkSize, this.file.size)
45
- const bytes = fileSlice.call(this.file, start, end)
46
- this.fileReader.readAsArrayBuffer(bytes)
47
- this.chunkIndex++
48
- return true
49
- } else {
50
- return false
51
- }
52
- }
53
- }
@@ -1,51 +0,0 @@
1
- export function getMetaValue(name) {
2
- const element = findElement(document.head, `meta[name="${name}"]`)
3
- if (element) {
4
- return element.getAttribute("content")
5
- }
6
- }
7
-
8
- export function findElements(root, selector) {
9
- if (typeof root == "string") {
10
- selector = root
11
- root = document
12
- }
13
- const elements = root.querySelectorAll(selector)
14
- return toArray(elements)
15
- }
16
-
17
- export function findElement(root, selector) {
18
- if (typeof root == "string") {
19
- selector = root
20
- root = document
21
- }
22
- return root.querySelector(selector)
23
- }
24
-
25
- export function dispatchEvent(element, type, eventInit = {}) {
26
- const { disabled } = element
27
- const { bubbles, cancelable, detail } = eventInit
28
- const event = document.createEvent("Event")
29
-
30
- event.initEvent(type, bubbles || true, cancelable || true)
31
- event.detail = detail || {}
32
-
33
- try {
34
- element.disabled = false
35
- element.dispatchEvent(event)
36
- } finally {
37
- element.disabled = disabled
38
- }
39
-
40
- return event
41
- }
42
-
43
- export function toArray(value) {
44
- if (Array.isArray(value)) {
45
- return value
46
- } else if (Array.from) {
47
- return Array.from(value)
48
- } else {
49
- return [].slice.call(value)
50
- }
51
- }
@@ -1,78 +0,0 @@
1
- import { Controller } from "@hotwired/stimulus";
2
- import { DirectUpload } from "./direct_upload_controller/direct_upload";
3
-
4
- const URL = window.URL || window.webkitURL
5
-
6
- function setupFilePreview(target, file){
7
- const onLoaded = function(){
8
- URL.revokeObjectURL(target.src)
9
- target.removeEventListener('load', onLoaded)
10
- }
11
- target.addEventListener('load', onLoaded)
12
- target.src = URL.createObjectURL(file)
13
- }
14
- export default class extends Controller {
15
- static targets = ['file', 'preview'];
16
- static values = {
17
- url: String,
18
- }
19
-
20
- initialize(){
21
- this.onFileChange = this.onFileChange.bind(this)
22
- }
23
-
24
- fileTargetConnected(target){
25
- if(!target.hiddenInput){
26
- const hiddenInput = document.createElement("input")
27
- hiddenInput.type = "hidden"
28
- hiddenInput.name = target.name
29
- target.insertAdjacentElement("beforebegin", hiddenInput)
30
- target.removeAttribute('name')
31
- target.hiddenInput = hiddenInput
32
- }
33
- target.addEventListener('change', this.onFileChange)
34
- }
35
-
36
- fileTargetDisconnected(target){
37
- target.removeEventListener('change', this.onFileChange)
38
- }
39
-
40
- onFileChange(event){
41
- const { target } = event
42
- const { hiddenInput, files } = target
43
- const file = files[0]
44
- if(!file) return
45
-
46
- const directUpload = new DirectUpload(file, this.urlValue, this)
47
-
48
- if(this.hasPreviewTarget){
49
- setupFilePreview(this.previewTarget, file)
50
- }
51
-
52
- directUpload.create((error, attributes) => {
53
- if(error){
54
- hiddenInput.removeAttribute('value')
55
- }else{
56
- hiddenInput.setAttribute('value', attributes.signed_id)
57
- }
58
- })
59
- }
60
-
61
- directUploadWillCreateBlobWithXHR(xhr) {
62
- this.dispatch("before-blob-request", { detail: xhr })
63
- }
64
-
65
- directUploadWillStoreFileWithXHR(xhr) {
66
- this.dispatch('started', { detail: xhr })
67
- this.dispatch('progress', { detail: { percent: 0 } })
68
- xhr.upload.addEventListener("progress", event => this.uploadRequestDidProgress(event))
69
- }
70
-
71
- uploadRequestDidProgress(event){
72
- const percent = event.loaded / event.total
73
- this.dispatch('progress', { detail: { percent } })
74
- if(percent == 1){
75
- this.dispatch('uploaded')
76
- }
77
- }
78
- }
@@ -1,5 +0,0 @@
1
- class TenantStorageService < ApplicationRecord
2
- belongs_to :tenant, inverse_of: :storage_services
3
-
4
- store :service_options, coder: ActiveRecord::Coders::JSON
5
- end
@@ -1,17 +0,0 @@
1
- class ActivestorageSaasTables < ActiveRecord::Migration[5.2]
2
- def change
3
- tenant_class = ActiveStorageSaas.tenant_class_name.constantize
4
- tenant_primary_key = tenant_class.columns.find{|col| col.name == tenant_class.primary_key }
5
- create_table :tenant_storage_services do |t|
6
- t.references :tenant, type: tenant_primary_key.type, foreign_key: { to_table: tenant_class.table_name }
7
- t.string :service_name
8
- t.json :service_options
9
-
10
- t.timestamps
11
- end
12
-
13
- add_reference tenant_class.table_name.to_sym, :tenant_storage_service, foreign_key: true
14
- add_reference :active_storage_blobs, :tenant, type: tenant_primary_key.type, foreign_key: true
15
- add_reference :active_storage_blobs, :tenant_storage_service, foreign_key: true
16
- end
17
- end
@@ -1,51 +0,0 @@
1
- require 'active_storage/service/saas_service'
2
-
3
- module ActiveStorageSaas
4
- module BlobPatch
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- belongs_to :tenant, class_name: ActiveStorageSaas.tenant_class_name # rubocop: disable Rails/ReflectionClassName
9
- belongs_to :tenant_storage_service, optional: true
10
-
11
- redefine_method :service do
12
- class_service = self.class.service
13
- if class_service.is_a?(ActiveStorage::Service::SaasService)
14
- @service ||= ActiveStorage::Service::SaasService.new(class_service.options.merge(blob: self))
15
- else
16
- class_service
17
- end
18
- end
19
- end
20
-
21
- def service_http_method_for_direct_upload
22
- service.respond_to?(:http_method_for_direct_upload) ? service.http_method_for_direct_upload : nil
23
- end
24
-
25
- def service_http_response_type_for_direct_upload
26
- service.respond_to?(:http_response_type_for_direct_upload) ? service.http_response_type_for_direct_upload : nil
27
- end
28
-
29
- # support sending data from form instead of headers
30
- def service_form_data_for_direct_upload(expires_in: service.url_expires_in)
31
- return {} unless service.respond_to?(:form_data_for_direct_upload)
32
-
33
- service.form_data_for_direct_upload(key,
34
- expires_in: expires_in,
35
- content_type: content_type,
36
- content_length: byte_size,
37
- checksum: checksum)
38
- end
39
-
40
- private
41
-
42
- def saas_service?
43
- service.is_a?(ActiveStorage::Service::SaasService)
44
- end
45
-
46
- def analyzer_class
47
- analyzers = saas_service? ? service.analyzers : ActiveStorage.analyzers
48
- analyzers.detect { |klass| klass.accept?(self) } || ActiveStorage::Analyzer::NullAnalyzer
49
- end
50
- end
51
- end
@@ -1,4 +0,0 @@
1
- module ActivestorageSaas
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end