mirror of
https://github.com/Luzifer/past3.git
synced 2024-11-10 00:40:00 +00:00
Fix remaining BS4 update, add private files feature
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
c31b86d22a
commit
0cba83022d
4 changed files with 101 additions and 49 deletions
71
app.js
71
app.js
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}))
|
||||||
|
|
||||||
|
|
55
index.html
55
index.html
|
@ -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,39 +26,31 @@
|
||||||
</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">
|
<a href="#" class="list-group-item active">
|
||||||
<span class="badge">2006-01-02 15:04:05</span>
|
<span class="badge">2006-01-02 15:04:05</span>
|
||||||
|
@ -66,8 +58,6 @@
|
||||||
</a>
|
</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">
|
||||||
<input type="text" class="form-control" id="file-url" placeholder="n/a" readonly>
|
<span class="input-group-text"><i class="fas fa-fw fa-link"></i> File-URL</span>
|
||||||
</div>
|
</div>
|
||||||
|
<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> <!-- /.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,14 +113,12 @@
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue