1
0
Fork 0
mirror of https://github.com/Luzifer/past3.git synced 2024-12-22 20:31:16 +00:00

Initial version

This commit is contained in:
Knut Ahlers 2017-01-14 21:07:48 +01:00
parent 7cf6b81c07
commit c86cc33de5
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
7 changed files with 393 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
public

17
Makefile Normal file
View file

@ -0,0 +1,17 @@
default: generate
generate: script index-page
script: public
coffee --bare --compile --output public app.coffee
python -m jsmin public/app.js > public/app.min.js
mv public/app.min.js public/app.js
index-page: public
./generate.py > public/index.html
public:
mkdir public
clean:
rm -rf public

161
app.coffee Normal file
View file

@ -0,0 +1,161 @@
$ ->
$('#signin').modal
backdrop: 'static'
keyboard: false
AWS.config.region = window.past3_config.region
CodeMirror.modeURL = 'https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.22.2/mode/%N/%N.js'
window.editor = CodeMirror.fromTextArea document.getElementById('editor'),
extraKeys:
Tab: (cm) -> cm.replaceSelection(Array(cm.getOption("indentUnit") + 1).join(" "))
lineNumbers: true
viewportMargin: 25
window.editor.setSize null, '100%'
$('#filename').bind 'input', filenameInput
$('#newFile').bind 'click', () ->
$('#filename').val('')
$('#file-url').val('n/a')
window.editor.setValue('')
$('.file-list-item').removeClass 'active'
return false
$('#saveFile').bind 'click', () ->
filename = $('#filename').val()
window.editor.save()
content = $('#editor').val()
saveFile(filename, content)
$('#file-url').bind 'click', () ->
$(this).select()
signinCallback = (authResult) ->
if authResult.Zi.id_token
AWS.config.credentials = new AWS.CognitoIdentityCredentials
IdentityPoolId: window.past3_config.identity_pool_id
Logins:
'accounts.google.com': authResult.Zi.id_token
AWS.config.credentials.get () ->
$('#signin').modal 'hide'
listFiles()
renderButton = () ->
gapi.signin2.render 'signInButton',
'scope': 'profile email',
'width': 240,
'height': 30,
'longtitle': true,
'theme': 'dark',
'onsuccess': signinCallback,
getFilePrefix = () ->
s3 = new AWS.S3()
"#{s3.config.credentials.identityId}/"
getFile = (filename) ->
s3 = new AWS.S3()
s3.getObject({
Bucket: window.past3_config.bucket
Key: getFilePrefix() + filename
}, loadFileIntoEditor)
$('#filename').val filename
$('#filename').trigger 'input'
listFiles = () ->
s3 = new AWS.S3()
s3.listObjects({
Bucket: window.past3_config.bucket
Prefix: getFilePrefix()
}, loadFileList)
saveFile = (filename, content) ->
mime = getMimeType filename
s3 = new AWS.S3()
s3.putObject({
ACL: window.past3_config.acl
Body: content
Bucket: window.past3_config.bucket
ContentType: mime.mime
Key: getFilePrefix() + filename
}, saveFileCallback)
loadFileIntoEditor = (err, data) ->
if err
error err
return
window.editor.setValue(String(data.Body))
loadFileList = (err, data) ->
if err
error err
return
$('.file-list-item').remove()
for obj in data.Contents
key = obj.Key.replace getFilePrefix(), ''
li = $("<a href='#' class='list-group-item file-list-item'><span class='badge'></span> #{key}</a>")
li.find('.badge').text formatDate(obj.LastModified)
li.data 'file', key
if key == $('#filename').val()
li.addClass 'active'
li.appendTo $('#fileList')
li.bind 'click', openFileClick
saveFileCallback = (err, data) ->
if err
error err
return
listFiles()
formatDate = (src) ->
$.format.date src, 'yyyy-MM-dd HH:mm:ss'
setEditorMime = (filename) ->
if window.mime_detect
window.clearTimeout window.mime_detect
window.mime_detect = window.setTimeout () ->
autoMime = getMimeType filename
window.editor.setOption 'mode', autoMime.mime
CodeMirror.autoLoadMode window.editor, autoMime.mode
, 500
getMimeType = (filename) ->
name_parts = filename.split('.')
ext = name_parts[name_parts.length - 1]
mime = CodeMirror.findModeByExtension ext
if mime == undefined
mime = CodeMirror.findModeByExtension 'txt'
return mime
openFileClick = () ->
getFile $(this).data('file')
$('.file-list-item').removeClass 'active'
$(this).addClass 'active'
return false
filenameInput = () ->
setEditorMime $(this).val()
$('#file-url').val window.past3_config.base_url + getFilePrefix() + $(this).val()
error = (err) ->
ed = $('#errorDisplay')
ed.find('.alert').text err
ed.show()

24
config.yml Normal file
View file

@ -0,0 +1,24 @@
---
# Canned ACL for files stored into S3. One of "private, public-read,
# public-read-write, authenticated-read, aws-exec-read, bucket-owner-read,
# bucket-owner-full-control"
acl: public-read
# 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
# interface
base_url: https://s3-eu-west-1.amazonaws.com/past3/
# Name of the Bucket used to store the files in. The bucket needs to be
# whitelisted for Cognito authenticated users
bucket: past3
# Id of the Cognito federated identity pool to be used to store users in
identity_pool_id: 'eu-west-1:8605f42e-f1e2-4c71-a796-a96ed9e79930'
# AWS region of the bucket
region: eu-west-1
# Client ID of the oAuth2 application created in the Google Cloud console
google_client_id: 693734536874-s0quna7oa2msnt1up4vubi4sh3uaucud.apps.googleusercontent.com

11
generate.py Executable file
View file

@ -0,0 +1,11 @@
#!/usr/bin/env python2
import yaml
from jinja2 import Environment, FileSystemLoader
config = yaml.load(open('config.yml', 'r').read())
env = Environment(loader=FileSystemLoader('./'))
template = env.get_template('index.html')
print(template.render(config))

176
index.html Normal file
View file

@ -0,0 +1,176 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>PaS(t)3 - S3 file editor</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.22.2/codemirror.min.css"
integrity="sha256-B4lcRQIA/hXjqRxuZHImRuHmb0IT1kscrY9mYJ7FsMs=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
<style>
#signInButton { margin: 20px 164px 0; }
.modal-body { text-align: center; }
#errorDisplay { display: none; }
</style>
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"
integrity="sha256-3Jy/GbSLrg0o9y5Z5n1uw0qxZECH7C6OQpVBgNFYa0g="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"
integrity="sha256-g6iAfvZp+nDQ2TdTR/VVKJf3bGro4ub5fvWSWVRi2NE="
crossorigin="anonymous"></script>
<![endif]-->
<!-- Sign-In with Google -->
<meta name="google-signin-client_id" content="{{ google_client_id }}">
<!-- Configure PaS(t)3 -->
<script>
window.past3_config = {
acl: '{{ acl }}',
base_url: '{{ base_url }}',
bucket: '{{ bucket }}',
identity_pool_id: '{{ identity_pool_id }}',
region: '{{ region }}',
};
</script>
</head>
<body>
<!-- NAV -->
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Pa<em>S(t)<strong>3</strong></em></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
</ul>
<ul class="nav navbar-nav navbar-right">
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div class="list-group" id="fileList">
<!--
<a href="#" class="list-group-item active">
<span class="badge">2006-01-02 15:04:05</span>
README.md
</a>
-->
</div>
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-10">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">Filename</div>
<input type="text" class="form-control" id="filename" placeholder="untitled">
</div>
</div>
</div> <!-- /.col-md-10 -->
<div class="col-md-2">
<button class="btn btn-default col-md-12" id="newFile"><i class="fa fa-file-o"></i> New File</button>
</div>
</div>
<div class="row">
<div class="col-md-10">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">File-URL</div>
<input type="text" class="form-control" id="file-url" placeholder="n/a" readonly>
</div>
</div>
</div> <!-- /.col-md-10 -->
<div class="col-md-2">
<button class="btn btn-success col-md-12" id="saveFile"><i class="fa fa-save"></i> Save</button>
</div>
</div>
<div class="row" id="errorDisplay">
<div class="col-md-12">
<div class="alert alert-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<textarea style="height:100%;width:100%;" id="editor"></textarea>
</div>
</div>
</div> <!-- /.col-md-9 -->
</div>
</div>
<div class="modal fade" tabindex="-1" role="dialog" id="signin">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">You need to sign in!</h4>
</div>
<div class="modal-body">
<p>
To get access to your files you need to sign in with your Google Account.
</p>
<div id="signInButton"></div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<a href="https://github.com/Luzifer/past3" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#70B7FD; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"
integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-dateFormat/1.0/jquery.dateFormat.min.js"
integrity="sha256-YVu3IT7nGTfxru7MQiv/TgOnffsbPuvXHRXuw1KzxWc=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"
integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/aws-sdk/2.7.21/aws-sdk.min.js"
integrity="sha256-hfu3qznG/BXuwrZTPWpq3eDm+eh6aao1eQMMSXgGYNg=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.22.2/codemirror.min.js"
integrity="sha256-pWigXb2fd8JQ4vx4hmK0s9m7u3z219t7tjcnmn76yd8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.22.2/addon/mode/loadmode.min.js"
integrity="sha256-bUg7jjJLHxp1tOgz4DdCaa/G3AQTx1RXhDHaz8y9bDA=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.22.2/mode/meta.min.js"
integrity="sha256-Q7TtpA3J4bkPZpylepA+IKKsUn0lfkek+EPrddqC1Lo=" crossorigin="anonymous"></script>
<script src="https://apis.google.com/js/platform.js?onload=renderButton" async defer></script>
<script src="app.js"></script>
</body>
</html>

3
requirements.txt Normal file
View file

@ -0,0 +1,3 @@
Jinja2==2.8
jsmin==2.2.1
PyYAML==3.12