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