add ipa support, part
This commit is contained in:
2
.fsw.yml
2
.fsw.yml
@@ -6,7 +6,7 @@ triggers:
|
||||
- '**/*.tmpl.html'
|
||||
env:
|
||||
DEBUG: "1"
|
||||
cmd: killall gohttpserver && go build && ./gohttpserver&
|
||||
cmd: (killall gohttpserver; true) && go build && ./gohttpserver&
|
||||
shell: true
|
||||
delay: 100ms
|
||||
signal: KILL
|
||||
|
||||
@@ -19,6 +19,7 @@ If using go1.5, ensure you set GO15VENDOREXPERIMENT=1
|
||||
1. [x] When only one dir under dir, path will combine two together
|
||||
1. [x] Directory zip download
|
||||
1. [ ] Apple ipa auto generate .plist file, qrcode can be recognized by iphone (Require https)
|
||||
1. [ ] Plist proxy
|
||||
1. [ ] Support modify the index page
|
||||
1. [ ] Download count statistics
|
||||
1. [x] CORS enabled
|
||||
@@ -27,6 +28,7 @@ If using go1.5, ensure you set GO15VENDOREXPERIMENT=1
|
||||
1. [ ] Global file search
|
||||
1. [x] Hidden work `download` and `qrcode` in small screen
|
||||
1. [x] Theme select support
|
||||
1. [ ] OK to working behide Nginx
|
||||
|
||||
## Installation
|
||||
```
|
||||
@@ -70,6 +72,7 @@ That's all. ^_^
|
||||
* Code Highlight <https://craig.is/making/rainbows>
|
||||
* Markdown-JS <https://github.com/showdownjs/showdown>
|
||||
* <https://github.com/sindresorhus/github-markdown-css>
|
||||
* <http://www.gorillatoolkit.org/pkg/handlers>
|
||||
|
||||
## LICENSE
|
||||
This project is under license [MIT](LICENSE)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -31,6 +32,11 @@ func NewHTTPStaticServer(root string, theme string) *HTTPStaticServer {
|
||||
m.HandleFunc("/-/raw/{path:.*}", s.hFileOrDirectory)
|
||||
m.HandleFunc("/-/zip/{path:.*}", s.hZip)
|
||||
m.HandleFunc("/-/json/{path:.*}", s.hJSONList)
|
||||
// routers for Apple *.ipa
|
||||
m.HandleFunc("/-/ipa/icon/{path:.*}", s.hIpaIcon)
|
||||
m.HandleFunc("/-/ipa/plist/{path:.*}", s.hPlist)
|
||||
// TODO: /ipa/link, /ipa/info
|
||||
|
||||
m.HandleFunc("/{path:.*}", s.hIndex).Methods("GET")
|
||||
return s
|
||||
}
|
||||
@@ -52,7 +58,56 @@ func (s *HTTPStaticServer) hIndex(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *HTTPStaticServer) hZip(w http.ResponseWriter, r *http.Request) {
|
||||
path := mux.Vars(r)["path"]
|
||||
CompressToZip(w, path)
|
||||
CompressToZip(w, filepath.Join(s.Root, path))
|
||||
}
|
||||
|
||||
func (s *HTTPStaticServer) hIpaIcon(w http.ResponseWriter, r *http.Request) {
|
||||
path := mux.Vars(r)["path"]
|
||||
relPath := filepath.Join(s.Root, path)
|
||||
data, err := parseIpaIcon(relPath)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusNotFound) // If parse icon error, 404 maybe the best way.
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (s *HTTPStaticServer) hPlist(w http.ResponseWriter, r *http.Request) {
|
||||
path := mux.Vars(r)["path"]
|
||||
// rename *.plist to *.ipa
|
||||
if filepath.Ext(path) == ".plist" {
|
||||
path = path[0:len(path)-6] + ".ipa"
|
||||
}
|
||||
|
||||
relPath := filepath.Join(s.Root, path)
|
||||
plinfo, err := parseIPA(relPath)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
scheme := "http"
|
||||
if r.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
ipaURL := url.URL{
|
||||
Scheme: scheme,
|
||||
Host: r.Host,
|
||||
Path: path,
|
||||
}
|
||||
imgURL := url.URL{
|
||||
Scheme: scheme,
|
||||
Host: r.Host,
|
||||
Path: filepath.Join("/-/ipa/icon", path),
|
||||
}
|
||||
// TODO: image ignore here.
|
||||
data, err := generateDownloadPlist(ipaURL.String(), imgURL.String(), plinfo)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/xml")
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (s *HTTPStaticServer) hFileOrDirectory(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
133
ipa.go
Normal file
133
ipa.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
goplist "github.com/DHowett/go-plist"
|
||||
)
|
||||
|
||||
type plistBundle struct {
|
||||
CFBundleIdentifier string `plist:"CFBundleIdentifier"`
|
||||
CFBundleVersion string `plist:"CFBundleVersion"`
|
||||
CFBundleDisplayName string `plist:"CFBundleDisplayName"`
|
||||
CFBundleIconFile string `plist:"CFBundleIconFile"`
|
||||
}
|
||||
|
||||
func parseIpaIcon(path string) (data []byte, err error) {
|
||||
iconPattern := regexp.MustCompile(`(?i)^Payload/[^/]*/icon\.png$`)
|
||||
r, err := zip.OpenReader(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
var zfile *zip.File
|
||||
for _, file := range r.File {
|
||||
if iconPattern.MatchString(file.Name) {
|
||||
zfile = file
|
||||
break
|
||||
}
|
||||
}
|
||||
if zfile == nil {
|
||||
err = errors.New("icon.png file not found")
|
||||
return
|
||||
}
|
||||
plreader, err := zfile.Open()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer plreader.Close()
|
||||
return ioutil.ReadAll(plreader)
|
||||
}
|
||||
|
||||
func parseIPA(path string) (plinfo *plistBundle, err error) {
|
||||
plistre := regexp.MustCompile(`^Payload/[^/]*/Info\.plist$`)
|
||||
r, err := zip.OpenReader(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
var plfile *zip.File
|
||||
for _, file := range r.File {
|
||||
if plistre.MatchString(file.Name) {
|
||||
plfile = file
|
||||
break
|
||||
}
|
||||
}
|
||||
if plfile == nil {
|
||||
err = errors.New("Info.plist file not found")
|
||||
return
|
||||
}
|
||||
plreader, err := plfile.Open()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer plreader.Close()
|
||||
buf := make([]byte, plfile.FileInfo().Size())
|
||||
_, err = io.ReadFull(plreader, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dec := goplist.NewDecoder(bytes.NewReader(buf))
|
||||
plinfo = new(plistBundle)
|
||||
err = dec.Decode(plinfo)
|
||||
return
|
||||
}
|
||||
|
||||
// ref: https://gist.github.com/frischmilch/b15d81eabb67925642bd#file_manifest.plist
|
||||
type plAsset struct {
|
||||
Kind string `plist:"kind"`
|
||||
URL string `plist:"url"`
|
||||
}
|
||||
|
||||
type plItem struct {
|
||||
Assets []*plAsset `plist:"assets"`
|
||||
Metadata struct {
|
||||
BundleIdentifier string `plist:"bundle-identifier"`
|
||||
BundleVersion string `plist:"bundle-version"`
|
||||
Kind string `plist:"kind"`
|
||||
Title string `plist:"title"`
|
||||
} `plist:"metadata"`
|
||||
}
|
||||
|
||||
type downloadPlist struct {
|
||||
Items []*plItem `plist:"items"`
|
||||
}
|
||||
|
||||
func generateDownloadPlist(ipaUrl, imgUrl string, plinfo *plistBundle) ([]byte, error) {
|
||||
dp := new(downloadPlist)
|
||||
item := new(plItem)
|
||||
item.Assets = append(item.Assets, &plAsset{
|
||||
Kind: "software-package",
|
||||
URL: ipaUrl,
|
||||
})
|
||||
|
||||
// FIXME(ssx): find icon from CFBundleIconFile
|
||||
_ = imgUrl
|
||||
// , &plAsset{
|
||||
// Kind: "display-image",
|
||||
// URL: imgUrl,
|
||||
// })
|
||||
|
||||
item.Metadata.Kind = "software"
|
||||
|
||||
item.Metadata.BundleIdentifier = plinfo.CFBundleIdentifier
|
||||
item.Metadata.BundleVersion = plinfo.CFBundleVersion
|
||||
item.Metadata.Title = plinfo.CFBundleDisplayName
|
||||
if item.Metadata.Title == "" {
|
||||
item.Metadata.Title = filepath.Base(ipaUrl)
|
||||
}
|
||||
|
||||
dp.Items = append(dp.Items, item)
|
||||
data, err := goplist.MarshalIndent(dp, goplist.XMLFormat, " ")
|
||||
// fmt.Println(string(data))
|
||||
// fmt.Println(err)
|
||||
return data, err
|
||||
}
|
||||
@@ -67,8 +67,6 @@
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Size</th>
|
||||
@@ -76,6 +74,8 @@
|
||||
<center>Actions</center>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="f in computedFiles">
|
||||
<td>
|
||||
<a v-on:click='clickFileOrDir(f, $event)' href="/{{f.path}}">
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
body {
|
||||
body {}
|
||||
|
||||
td>a:hover {
|
||||
color: #1fa67a;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #1fa67a;
|
||||
td>a {
|
||||
color: rgb(51, 51, 51);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.navbar-inverse {
|
||||
|
||||
Reference in New Issue
Block a user