1
0
mirror of https://github.com/Luzifer/past3.git synced 2024-09-19 17:02:59 +00:00

Fix remaining BS4 update, add private files feature

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2018-09-25 01:18:01 +02:00
parent c31b86d22a
commit 0cba83022d
Signed by: luzifer
GPG Key ID: DC2729FDD34BE99E
4 changed files with 101 additions and 49 deletions

71
app.js
View File

@ -1,3 +1,4 @@
const allUsersURI = 'http://acs.amazonaws.com/groups/global/AllUsers'
const dateFormat = 'YYYY-MM-DD HH:mm:ss' const dateFormat = 'YYYY-MM-DD HH:mm:ss'
// showAlert creates an alert message and displays it // showAlert creates an alert message and displays it
@ -47,9 +48,8 @@ function fileActionCallback(err, data) {
} }
// filenameInput is the callback for changes in the filename // filenameInput is the callback for changes in the filename
function filenameInput() { function filenameInput(e) {
setEditorMime($(this).val()) updateFileURL()
$('#file-url').val(window.past3_config.base_url + getFilePrefix() + $(this).val())
} }
// formatDate formats a Date() object into iso-like format // formatDate formats a Date() object into iso-like format
@ -95,8 +95,26 @@ function getFile(filename) {
Key: getFilePrefix() + filename, Key: getFilePrefix() + filename,
}, loadFileIntoEditor) }, loadFileIntoEditor)
// Check whether the file is public
s3.getObjectAcl({
Bucket: window.past3_config.bucket,
Key: getFilePrefix() + filename,
}, (err, data) => {
if (err) return error(err)
for (let grant of data.Grants) {
if (grant.Grantee.Type == "Group" && grant.Grantee.URI == allUsersURI) {
$('#acl').data('public', true)
return updateFileURL()
}
}
$('#acl').data('public', false)
return updateFileURL()
})
$('#filename').val(filename) $('#filename').val(filename)
$('#filename').trigger('input') updateFileURL()
} }
// getFilePrefix retrieves the file prefix using the Cognito identityId // getFilePrefix retrieves the file prefix using the Cognito identityId
@ -165,7 +183,17 @@ function init() {
if (cf) cf.find('i').removeClass('fa-file').addClass('fa-file-upload') if (cf) cf.find('i').removeClass('fa-file').addClass('fa-file-upload')
}) })
$('#acl').data('public', window.past3_config.default_public)
// Set up bindings // Set up bindings
$('#acl').bind('click', (e) => {
let el = $(e.target)
el.data('public', !el.data('public'))
updateFileURL()
return false
})
$('#filename').bind('input', filenameInput) $('#filename').bind('input', filenameInput)
$('#newFile').bind('click', () => { $('#newFile').bind('click', () => {
@ -215,6 +243,12 @@ function init() {
e.preventDefault() e.preventDefault()
return false return false
} }
if ((e.metaKey || e.ctrlKey) && e.keyCode == 76) { // cmd + l / ctrl + l
$('#acl').trigger('click')
e.preventDefault()
return false
}
}) })
} }
@ -274,10 +308,12 @@ function loadFileList(err, data) {
fileIcon = 'file' fileIcon = 'file'
} }
let li = $(`<a href='#${key}' class='list-group-item file-list-item'><span class='badge'></span> <i class="fas fa-fw fa-${fileIcon}"></i> ${key}</a>`) let li = $(`<a href='#${key}' class='list-group-item file-list-item d-flex justify-content-between align-items-center'><span><i class="fas fa-fw fa-${fileIcon}"></i> ${key}</span></a>`)
li.find('.badge').text(`${formatDate(obj.LastModified)}`)
li.data('file', key) li.data('file', key)
let badge = $(`<span class='badge badge-dark badge-pill'>${formatDate(obj.LastModified)}</span>`)
badge.appendTo(li)
if (key === $('#filename').val()) { if (key === $('#filename').val()) {
li.addClass('active') li.addClass('active')
} }
@ -308,13 +344,16 @@ function renderButton() {
// saveFile saves the editor content into the S3 bucket // saveFile saves the editor content into the S3 bucket
function saveFile(filename, content) { function saveFile(filename, content) {
let mime = getMimeType(filename) let mime = getMimeType(filename)
let pub = $('#acl').data('public')
let s3 = new AWS.S3() let s3 = new AWS.S3()
s3.putObject({ s3.putObject({
ACL: window.past3_config.acl, ACL: pub ? 'public-read' : 'private',
Body: content, Body: content,
Bucket: window.past3_config.bucket, Bucket: window.past3_config.bucket,
ContentType: mime.mime, ContentType: mime.mime,
Key: getFilePrefix() + filename, Key: getFilePrefix() + filename,
ServerSideEncryption: 'AES256',
}, saveFileCallback) }, saveFileCallback)
} }
@ -339,7 +378,7 @@ function setEditorMime(filename) {
let autoMime = getMimeType(filename) let autoMime = getMimeType(filename)
window.editor.setOption('mode', autoMime.mime) window.editor.setOption('mode', autoMime.mime)
CodeMirror.autoLoadMode(window.editor, autoMime.mode) CodeMirror.autoLoadMode(window.editor, autoMime.mode)
}, 500) }, 100)
} }
// signinCallback is triggered by the Google sign-in button // signinCallback is triggered by the Google sign-in button
@ -355,6 +394,22 @@ function signinCallback(authResult) {
} }
} }
// updateFileURL sets the public URL of the file and adjusts the ACL button
function updateFileURL() {
let pub = $('#acl').data('public')
let filename = $('#filename').val()
setEditorMime(filename)
if (pub) {
$('#file-url').val(window.past3_config.base_url + getFilePrefix() + filename)
$('#acl').find('i').removeClass('fa-unlock').addClass('fa-lock')
} else {
$('#file-url').val('File is private, no URL available')
$('#acl').find('i').removeClass('fa-lock').addClass('fa-unlock')
}
}
// Initialize app on document ready // Initialize app on document ready
$(() => init()) $(() => init())

View File

@ -1,9 +1,8 @@
--- ---
# Canned ACL for files stored into S3. One of "private, public-read, # Default acl setting for new files: If set to true the files are created
# public-read-write, authenticated-read, aws-exec-read, bucket-owner-read, # using `public-read` acl, if set to false they are created with `private` acl.
# bucket-owner-full-control" default_public: true
acl: public-read
# Base URL for the uploaded files, either S3 download URL or CloudFront # Base URL for the uploaded files, either S3 download URL or CloudFront
# distribution URL to which the file path is appended for display in the # distribution URL to which the file path is appended for display in the
@ -12,7 +11,7 @@ base_url: https://paste.luzifer.io/
# Theme name (lowercase) of Bootswatch.com theme (Optional, when not # Theme name (lowercase) of Bootswatch.com theme (Optional, when not
# specified the original bootstrap theme is used) # specified the original bootstrap theme is used)
bootswatch_theme: cosmo bootswatch_theme: flatly
# Name of the Bucket used to store the files in. The bucket needs to be # Name of the Bucket used to store the files in. The bucket needs to be
# whitelisted for Cognito authenticated users # whitelisted for Cognito authenticated users

View File

@ -7,5 +7,4 @@ config = yaml.load(open('config.yml', 'r').read())
env = Environment(loader=FileSystemLoader('./')) env = Environment(loader=FileSystemLoader('./'))
template = env.get_template('index.html') template = env.get_template('index.html')
print(template.render(config)) print(template.render({'config': config}))

View File

@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>PaS(t)3 - S3 file editor</title> <title>PaS(t)3 - S3 file editor</title>
{% if bootswatch_theme %} {% if config.bootswatch_theme %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.7/{{ bootswatch_theme }}/bootstrap.min.css" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/4.1.3/{{ config.bootswatch_theme }}/bootstrap.min.css" />
{% else %} {% else %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css"
integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE=" crossorigin="anonymous" /> integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE=" crossorigin="anonymous" />
@ -26,47 +26,37 @@
</style> </style>
<!-- Sign-In with Google --> <!-- Sign-In with Google -->
<meta name="google-signin-client_id" content="{{ google_client_id }}"> <meta name="google-signin-client_id" content="{{ config.google_client_id }}">
<!-- Configure PaS(t)3 --> <!-- Configure PaS(t)3 -->
<script> <script>
window.past3_config = { window.past3_config = {{ config|tojson }};
acl: '{{ acl }}',
base_url: '{{ base_url }}',
bucket: '{{ bucket }}',
identity_pool_id: '{{ identity_pool_id }}',
region: '{{ region }}',
};
</script> </script>
</head> </head>
<body> <body>
<!-- NAV --> <!-- NAV -->
<nav class="navbar navbar-default"> <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand" href="#"> <a class="navbar-brand" href="#">
<i class="fas fa-fw fa-clipboard-list"></i> <i class="fas fa-fw fa-clipboard-list"></i>
Pa<em>S(t)<strong>3</strong></em> Pa<em>S(t)<strong>3</strong></em>
</a> </a>
</nav> </nav>
<div class="container-fluid"> <div class="container-fluid mt-3">
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
<div class="panel panel-default"> <div class="card">
<div class="panel-heading"> <div class="card-header">
<i class="fas fa-fw fa-file"></i> Files in your namespace <i class="fas fa-fw fa-file"></i> Files in your namespace
</div> </div>
<div class="panel-body"> <div class="list-group list-group-flush" id="fileList">
<!--
<div class="list-group" id="fileList"> <a href="#" class="list-group-item active">
<!-- <span class="badge">2006-01-02 15:04:05</span>
<a href="#" class="list-group-item active"> README.md
<span class="badge">2006-01-02 15:04:05</span> </a>
README.md -->
</a>
-->
</div>
</div> </div>
</div> </div>
</div> </div>
@ -75,31 +65,42 @@
<div class="row"> <div class="row">
<div class="col-md-10"> <div class="col-md-10">
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-addon"><i class="fas fa-fw fa-tag"></i> Filename</div> <div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-tag"></i> Filename</span>
</div>
<input type="text" class="form-control" id="filename" placeholder="untitled"> <input type="text" class="form-control" id="filename" placeholder="untitled">
</div> </div>
</div> </div>
</div> <!-- /.col-md-10 --> </div> <!-- /.col-md-10 -->
<div class="col-md-2"> <div class="col-md-2">
<button class="btn btn-default col-md-12" id="newFile"><i class="fas fa-fw fa-file"></i> New File</button> <button class="btn btn-primary col-md-12" id="newFile" title="New File (Ctrl + N)"><i class="fas fa-fw fa-file"></i> New File</button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-10"> <div class="col-md-10">
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-addon"><i class="fas fa-fw fa-link"></i> File-URL</div> <div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-link"></i> File-URL</span>
</div>
<input type="text" class="form-control" id="file-url" placeholder="n/a" readonly> <input type="text" class="form-control" id="file-url" placeholder="n/a" readonly>
<div class="input-group-append">
<button type="button" class="btn btn-secondary" id="acl" title="Change ACL (Ctrl + L)"><i class="fas fa-fw fa-lock"></i></button>
</div>
</div> </div>
</div> </div>
</div> <!-- /.col-md-10 --> </div> <!-- /.col-md-10 -->
<div class="col-md-2"> <div class="col-md-2">
<div class="btn-group file-mgmt" role="group"> <div class="btn-group file-mgmt" role="group">
<button type="button" class="btn btn-success col-md-9" id="saveFile"><i class="fas fa-fw fa-save"></i> Save</button> <button type="button" class="btn btn-success col-md-9" id="saveFile" title="Save (Ctrl + S)"><i class="fas fa-fw fa-save"></i> Save</button>
<button type="button" class="btn btn-danger col-md-3" id="deleteFile"><i class="fas fa-fw fa-trash"></i></button> <button type="button" class="btn btn-danger col-md-3" id="deleteFile"><i class="fas fa-fw fa-trash"></i></button>
</div> </div>
</div> </div>
@ -112,13 +113,11 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-default"> <div class="card">
<div class="panel-heading"> <div class="card-header">
<i class="fas fa-fw fa-file-alt"></i> File content <i class="fas fa-fw fa-file-alt"></i> File content
</div> </div>
<div class="panel-body"> <textarea style="height:100%;width:100%;" id="editor"></textarea>
<textarea style="height:100%;width:100%;" id="editor"></textarea>
</div>
</div> </div>
</div> </div>