There was no way to put a file up for public use short of running an S3 client with the uploader key. Add a "files" area to the admin: upload one or more files, copy the link, edit the note, replace the contents under the same link, delete. It has its own read/write/delete permission flags like the other areas, and every action is audited. The objects go to the storage stack's public bucket and are served by Garage at s3.asxp.io/public/<key>, not by this app. The app's key can only put and delete there, so a public_files table is the record of what was published. Keys get a random prefix: links are not guessable and names never collide. That host is same-site with the admin, so a file that can carry script must not render there. Only a fixed list of types (images, PDF, plain text, audio, video) is served inline; the rest, HTML and SVG included, is stored as an attachment. Image metadata is stripped by default, as for the bingo photos, because of GPS tags. A per-file limit and a total quota keep the small disk on asxpio-ops from filling up. Assisted-by: Claude:fable-5-2
20 lines
838 B
Ruby
20 lines
838 B
Ruby
Sequel.migration do
|
|
change do
|
|
# Index of what the admin uploaded to the public bucket (/admin/files).
|
|
# The bucket cannot be listed with the app's key, so this is the only
|
|
# record of those objects.
|
|
create_table(:public_files) do
|
|
primary_key :id
|
|
String :key, null: false, unique: true # object key: <8 hex>/<filename>
|
|
String :filename, null: false # sanitised, as in the key
|
|
String :content_type, null: false
|
|
TrueClass :inline, null: false, default: false # false = served as a download
|
|
Bignum :size, null: false
|
|
String :sha256, null: false
|
|
String :note, text: true
|
|
String :uploaded_by
|
|
DateTime :created_at, null: false
|
|
DateTime :updated_at, null: false
|
|
end
|
|
end
|
|
end
|