Initial commit
This commit is contained in:
commit
03e9afc888
9 changed files with 243 additions and 0 deletions
2
.cfignore
Normal file
2
.cfignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
vendor
|
||||||
|
README.md
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
vendor
|
||||||
|
glide.lock
|
10
README.md
Normal file
10
README.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# S3 Manager
|
||||||
|
|
||||||
|
Manage S3 buckets from any provider.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
* `S3_ACCESS_KEY_ID`
|
||||||
|
* `S3_SECRET_ACCESS_KEY`
|
||||||
|
* `S3_ENDPOINT`: Optional. In case you are using a different S3 provider than AWS. Defaults to `s3.amazonaws.com`
|
||||||
|
* `V2_SIGNING`: Optional. In case your S3 provider still uses V2 Signing, set this to `true`
|
4
glide.yaml
Normal file
4
glide.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package: github.com/mastertinner/s3manager
|
||||||
|
import:
|
||||||
|
- package: github.com/minio/minio-go
|
||||||
|
version: ^2.0.2
|
64
handlers.go
Normal file
64
handlers.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
minio "github.com/minio/minio-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// indexHandler handles the main page
|
||||||
|
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
lp := path.Join("templates", "layout.html")
|
||||||
|
ip := path.Join("templates", "index.html")
|
||||||
|
|
||||||
|
t, err := template.ParseFiles(lp, ip)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buckets, err := minioClient.ListBuckets()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.ExecuteTemplate(w, "layout", buckets)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bucketHandler handles the main page
|
||||||
|
func bucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
bucket := strings.Split(r.URL.Path, "/")[2]
|
||||||
|
var objects []minio.ObjectInfo
|
||||||
|
|
||||||
|
lp := path.Join("templates", "layout.html")
|
||||||
|
bp := path.Join("templates", "bucket.html")
|
||||||
|
|
||||||
|
t, err := template.ParseFiles(lp, bp)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a done channel to control 'ListObjectsV2' go routine.
|
||||||
|
doneCh := make(chan struct{})
|
||||||
|
|
||||||
|
objectCh := minioClient.ListObjectsV2(bucket, "", false, doneCh)
|
||||||
|
for object := range objectCh {
|
||||||
|
if object.Err != nil {
|
||||||
|
fmt.Println(object.Err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
objects = append(objects, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.ExecuteTemplate(w, "layout", objects)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
48
main.go
Normal file
48
main.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/minio/minio-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var minioClient *minio.Client
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
port := os.Getenv("PORT")
|
||||||
|
if len(port) == 0 {
|
||||||
|
port = "8080"
|
||||||
|
}
|
||||||
|
s3AccessKeyID := os.Getenv("S3_ACCESS_KEY_ID")
|
||||||
|
if len(s3AccessKeyID) == 0 {
|
||||||
|
log.Fatalln("Please set S3_ACCESS_KEY_ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
s3SecretAccessKey := os.Getenv("S3_SECRET_ACCESS_KEY")
|
||||||
|
if len(s3SecretAccessKey) == 0 {
|
||||||
|
log.Fatalln("Please set S3_SECRET_ACCESS_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
s3Endpoint := os.Getenv("S3_ENDPOINT")
|
||||||
|
if len(s3Endpoint) == 0 {
|
||||||
|
s3Endpoint = "s3.amazonaws.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("V2_SIGNING") == "true" {
|
||||||
|
minioClient, err = minio.NewV2(s3Endpoint, s3AccessKeyID, s3SecretAccessKey, true)
|
||||||
|
} else {
|
||||||
|
minioClient, err = minio.New(s3Endpoint, s3AccessKeyID, s3SecretAccessKey, true)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc("/", indexHandler)
|
||||||
|
http.HandleFunc("/buckets/", bucketHandler)
|
||||||
|
|
||||||
|
log.Fatal(http.ListenAndServe(":"+port, nil))
|
||||||
|
}
|
25
templates/bucket.html
Normal file
25
templates/bucket.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{{ define "content" }}
|
||||||
|
<table class="striped">
|
||||||
|
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Key</th>
|
||||||
|
<th>Size</th>
|
||||||
|
<th>Owner</th>
|
||||||
|
<th>Last Modified</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{ range $object := . }}
|
||||||
|
<tr>
|
||||||
|
<td>{{ $object.Key }}</td>
|
||||||
|
<td>{{ $object.Size }} bytes</td>
|
||||||
|
<td>{{ $object.Owner }}</td>
|
||||||
|
<td>{{ $object.LastModified }}</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
{{ end }}
|
54
templates/index.html
Normal file
54
templates/index.html
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
{{ define "content" }}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col m12 l6">
|
||||||
|
|
||||||
|
{{ range $bucket := . }}
|
||||||
|
<a href="/buckets/{{ $bucket.Name }}" style="color: black;">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="row" style="margin-bottom: 0;">
|
||||||
|
<div class="col">
|
||||||
|
<i class="material-icons large">cloud_circle</i>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<span class="card-title">
|
||||||
|
{{ $bucket.Name }}
|
||||||
|
</span>
|
||||||
|
<p style="color: gray;">Created on {{ $bucket.CreationDate }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fixed-action-btn">
|
||||||
|
<a class="btn-floating btn-large red" href="#modal-create-bucket">
|
||||||
|
<i class="large material-icons">add</i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="modal-create-bucket" class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h4>Create Bucket</h4>
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s6">
|
||||||
|
<form action="/api/buckets" method="POST">
|
||||||
|
<div class="input-field">
|
||||||
|
<input placeholder="My Bucket" id="name" type="text">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Create</a>
|
||||||
|
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
34
templates/layout.html
Normal file
34
templates/layout.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{{ define "layout" }}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>
|
||||||
|
|
||||||
|
<title>S3 Manager</title>
|
||||||
|
|
||||||
|
<link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.8/css/materialize.min.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<nav class="purple" role="navigation">
|
||||||
|
<div class="nav-wrapper container">
|
||||||
|
<a href="/" class="brand-logo">S3 Manager</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="section">
|
||||||
|
{{ template "content" . }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.8/js/materialize.min.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
Loading…
Reference in a new issue