mirror of
https://github.com/pldubouilh/gossa.git
synced 2026-08-28 12:56:35 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8bfc21902 | ||
|
|
cd6122b393 | ||
|
|
5a1f75265d | ||
|
|
d4a60b3ece | ||
|
|
c0d7616101 | ||
|
|
83038f6de2 | ||
|
|
a7132076cb | ||
|
|
f384c3025b | ||
|
|
5fbc140e53 | ||
|
|
1861de0d57 | ||
|
|
50c524cc9b | ||
|
|
99a6aec8db | ||
|
|
33c93fc0b4 | ||
|
|
ec06354eb0 | ||
|
|
b92197cf0f |
@@ -9,7 +9,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.19.1
|
||||
go-version: 1.23.0
|
||||
id: go
|
||||
|
||||
- name: deps
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.19.1
|
||||
go-version: 1.23.0
|
||||
id: go
|
||||
|
||||
- name: Set env
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.19.1
|
||||
go-version: 1.23.0
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FLAGS := -ldflags "-s -w" -trimpath
|
||||
FLAGS := -trimpath
|
||||
NOCGO := CGO_ENABLED=0
|
||||
|
||||
build::
|
||||
@@ -59,12 +59,13 @@ watch-test::
|
||||
ls gossa.go gossa_test.go ui/* | entr -rc make test
|
||||
|
||||
build-all:: build
|
||||
${NOCGO} GOOS=linux GOARCH=amd64 go build ${FLAGS} -o builds/gossa-linux-x64
|
||||
${NOCGO} GOOS=linux GOARCH=arm go build ${FLAGS} -o builds/gossa-linux-arm
|
||||
${NOCGO} GOOS=linux GOARCH=arm64 go build ${FLAGS} -o builds/gossa-linux-arm64
|
||||
${NOCGO} GOOS=darwin GOARCH=amd64 go build ${FLAGS} -o builds/gossa-mac-x64
|
||||
${NOCGO} GOOS=darwin GOARCH=arm64 go build ${FLAGS} -o builds/gossa-mac-arm64
|
||||
${NOCGO} GOOS=windows GOARCH=amd64 go build ${FLAGS} -o builds/gossa-windows.exe
|
||||
go version
|
||||
${NOCGO} GOOS=linux GOARCH=amd64 go build ${FLAGS} -o builds/gossa-linux-x64
|
||||
${NOCGO} GOOS=linux GOARCH=arm go build ${FLAGS} -o builds/gossa-linux-arm
|
||||
${NOCGO} GOOS=linux GOARCH=arm64 go build ${FLAGS} -o builds/gossa-linux-arm64
|
||||
${NOCGO} GOOS=darwin GOARCH=amd64 go build ${FLAGS} -o builds/gossa-mac-x64
|
||||
${NOCGO} GOOS=darwin GOARCH=arm64 go build ${FLAGS} -o builds/gossa-mac-arm64
|
||||
${NOCGO} GOOS=windows GOARCH=amd64 go build ${FLAGS} -o builds/gossa-windows.exe
|
||||
sha256sum builds/*
|
||||
|
||||
clean::
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -25,7 +24,7 @@ import (
|
||||
|
||||
type rowTemplate struct {
|
||||
Name string
|
||||
Href template.HTML
|
||||
Href template.URL
|
||||
Size string
|
||||
Ext string
|
||||
}
|
||||
@@ -83,9 +82,9 @@ func humanize(bytes int64) string {
|
||||
}
|
||||
|
||||
func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path string) {
|
||||
_files, err := ioutil.ReadDir(fullPath)
|
||||
files, err := os.ReadDir(fullPath)
|
||||
check(err)
|
||||
sort.Slice(_files, func(i, j int) bool { return strings.ToLower(_files[i].Name()) < strings.ToLower(_files[j].Name()) })
|
||||
sort.Slice(files, func(i, j int) bool { return strings.ToLower(files[i].Name()) < strings.ToLower(files[j].Name()) })
|
||||
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path += "/"
|
||||
@@ -100,20 +99,20 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
|
||||
p.Ro = *ro
|
||||
p.Title = template.HTML(html.EscapeString(title))
|
||||
|
||||
for _, el := range _files {
|
||||
if *skipHidden && strings.HasPrefix(el.Name(), ".") {
|
||||
continue // dont print hidden files if we're not allowed
|
||||
}
|
||||
if !*symlinks && el.Mode()&os.ModeSymlink != 0 {
|
||||
continue // dont print symlinks if were not allowed
|
||||
}
|
||||
|
||||
el, err := os.Stat(fullPath + "/" + el.Name())
|
||||
for _, el := range files {
|
||||
info, err := el.Info()
|
||||
if err != nil {
|
||||
log.Println("error - cant stat a file", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if *skipHidden && strings.HasPrefix(el.Name(), ".") {
|
||||
continue // dont print hidden files if we're not allowed
|
||||
}
|
||||
if *symlinks && info.Mode()&os.ModeSymlink != 0 {
|
||||
continue // dont follow symlinks if we're not allowed
|
||||
}
|
||||
|
||||
href := url.PathEscape(el.Name())
|
||||
name := el.Name()
|
||||
|
||||
@@ -122,11 +121,13 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
|
||||
}
|
||||
|
||||
if el.IsDir() {
|
||||
p.RowsFolders = append(p.RowsFolders, rowTemplate{name + "/", template.HTML(href), "", "folder"})
|
||||
row := rowTemplate{name + "/", template.URL(href), "", "folder"}
|
||||
p.RowsFolders = append(p.RowsFolders, row)
|
||||
} else {
|
||||
sl := strings.Split(name, ".")
|
||||
ext := strings.ToLower(sl[len(sl)-1])
|
||||
p.RowsFiles = append(p.RowsFiles, rowTemplate{name, template.HTML(href), humanize(el.Size()), ext})
|
||||
row := rowTemplate{name, template.URL(href), humanize(info.Size()), ext}
|
||||
p.RowsFiles = append(p.RowsFiles, row)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,9 +137,9 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
|
||||
gz, err := gzip.NewWriterLevel(w, gzip.BestSpeed) // BestSpeed is Much Faster than default - base on a very unscientific local test, and only ~30% larger (compression remains still very effective, ~6x)
|
||||
check(err)
|
||||
defer gz.Close()
|
||||
templateParsed.Execute(gz, p)
|
||||
tmpl.Execute(gz, p)
|
||||
} else {
|
||||
templateParsed.Execute(w, p)
|
||||
tmpl.Execute(w, p)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +227,7 @@ func rpc(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var rpc rpcCall
|
||||
defer exitPath(w, "rpc", rpc)
|
||||
bodyBytes, err := ioutil.ReadAll(r.Body)
|
||||
bodyBytes, err := io.ReadAll(r.Body)
|
||||
check(err)
|
||||
json.Unmarshal(bodyBytes, &rpc)
|
||||
|
||||
|
||||
Executable → Regular
+13
-5
@@ -17,10 +17,18 @@ var styleCss string
|
||||
var faviconSvg []byte
|
||||
|
||||
//go:embed ui/ui.tmpl
|
||||
var template0 string
|
||||
var uiTmpl string
|
||||
|
||||
var tmpl *template.Template
|
||||
|
||||
// fill in template
|
||||
var template1 = strings.Replace(template0, "css_will_be_here", styleCss, 1)
|
||||
var template2 = strings.Replace(template1, "js_will_be_here", scriptJs, 1)
|
||||
var template3 = strings.Replace(template2, "favicon_will_be_here", base64.StdEncoding.EncodeToString(faviconSvg), 2)
|
||||
var templateParsed, _ = template.New("").Parse(template3)
|
||||
func init() {
|
||||
var err error
|
||||
t := strings.Replace(uiTmpl, "css_will_be_here", styleCss, 1)
|
||||
t = strings.Replace(t, "js_will_be_here", scriptJs, 1)
|
||||
t = strings.Replace(t, "favicon_will_be_here", base64.StdEncoding.EncodeToString(faviconSvg), 2)
|
||||
tmpl, err = template.New("").Parse(t)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,17 @@ func doTestRegular(t *testing.T, url string, testExtra bool) {
|
||||
t.Fatal("post file incorrect path didnt errored")
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~
|
||||
fmt.Println("\r\n~~~~~~~~~~ test post file")
|
||||
path = "2024-01-02-10:36:58.png"
|
||||
payload = "123123123123123123123123"
|
||||
body0 = postDummyFile(t, url, path, payload)
|
||||
body1 = get(t, url+path)
|
||||
body2 = fetchAndTestDefault(t, url)
|
||||
if body0 != `ok` || body1 != payload || !strings.Contains(body2, `href="2024-01-02-10:36:58.png"`) {
|
||||
t.Fatal("post file errored")
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~
|
||||
fmt.Println("\r\n~~~~~~~~~~ test mv rpc")
|
||||
body0 = postJSON(t, url+"rpc", `{"call":"mv","args":["/AAA", "/hols/AAA"]}`)
|
||||
|
||||
@@ -29,7 +29,9 @@ a simple UI comes as default, featuring :
|
||||
|
||||
[nix](https://search.nixos.org/packages?channel=unstable&show=gossa&from=0&size=50&sort=relevance&type=packages&query=gossa) - e.g. `nix-shell -p gossa`
|
||||
|
||||
binaries are available on the [release page](https://github.com/pldubouilh/gossa/releases) - or simply `make build` this repo.
|
||||
[mpr](https://mpr.makedeb.org/packages/gossa)
|
||||
|
||||
binaries are available on the [release page](https://github.com/pldubouilh/gossa/releases)
|
||||
|
||||
all builds are reproducible, checkout the hashes on the release page.
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
FROM golang:1.18 as builder
|
||||
FROM golang:1.23.0 as builder
|
||||
WORKDIR /gossaSrc
|
||||
COPY . /gossaSrc
|
||||
RUN cd /gossaSrc && make
|
||||
RUN make
|
||||
|
||||
FROM alpine:3.15
|
||||
FROM docker.io/library/alpine:3.20
|
||||
ENV UID="1000" GID="1000" HOST="0.0.0.0" PORT="8001" PREFIX="/" FOLLOW_SYMLINKS="false" SKIP_HIDDEN_FILES="true" DATADIR="/shared" READONLY="false" VERB="false"
|
||||
RUN apk add --no-cache su-exec
|
||||
COPY ./support/entrypoint.sh /entrypoint.sh
|
||||
COPY --from=builder /gossaSrc/gossa /gossa
|
||||
ENTRYPOINT "/entrypoint.sh"
|
||||
RUN addgroup -g ${GID} user \
|
||||
&& adduser -D -u ${UID} -G user user
|
||||
WORKDIR ${DATADIR}
|
||||
RUN chown ${UID}:${GID} ${DATADIR}
|
||||
USER ${UID}:${GID}
|
||||
ENTRYPOINT /gossa -h ${HOST} -p ${PORT} -k=${SKIP_HIDDEN_FILES} -ro=${READONLY} --symlinks=${FOLLOW_SYMLINKS} --prefix=${PREFIX} --verb=${VERB} ${DATADIR}
|
||||
HEALTHCHECK --timeout=5s --start-period=5s --retries=3 CMD wget --no-verbose --tries=1 --spider 127.0.0.1:8001 || exit 1
|
||||
@@ -2,9 +2,29 @@ version: '2'
|
||||
|
||||
services:
|
||||
gossa-server:
|
||||
image: pldubouilh/gossa
|
||||
image: docker.io/pldubouilh/gossa:latest
|
||||
container_name: gossa
|
||||
restart: always
|
||||
read_only: true
|
||||
# uncomment to set the user
|
||||
# user: "1000:1000"
|
||||
# userns_mode: "keep-id" # uncomment if using rootless podman as well as the x-podman directive at the bottom
|
||||
# environment:
|
||||
#- READONLY=true # uncomment to set gossa as read only
|
||||
# - UID=1000 # this should match the user set above
|
||||
# - GID=1000 # this should match the user's group
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- SETGID
|
||||
- SETUID
|
||||
# uncomment to set resource usage limits
|
||||
# deploy:
|
||||
# resources:
|
||||
# limits:
|
||||
# cpus: "2"
|
||||
# memory: 250m
|
||||
# pids: 1024
|
||||
ports:
|
||||
- 8001:8001
|
||||
volumes:
|
||||
@@ -14,3 +34,6 @@ services:
|
||||
# - "traefik.port=8001"
|
||||
# - "traefik.backend=gossa"
|
||||
# - "traefik.frontend.rule=Host:${GOSSA}.${DOMAIN}"
|
||||
|
||||
# x-podman: # uncomment if using rootless podman as well as the userns_mode directive at the top
|
||||
# in_pod: false
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
exec su-exec ${UID}:${GID} /gossa -h ${HOST} -p ${PORT} -k=${SKIP_HIDDEN_FILES} -ro=${READONLY} --symlinks=${FOLLOW_SYMLINKS} --prefix=${PREFIX} --verb=${VERB} ${DATADIR}
|
||||
+100
-31
@@ -38,36 +38,67 @@ if you prefer building the image yourself :
|
||||
|
||||
a docker-compose example image is also provided. running docker compose should be straightforward : `docker-compose up .` have a look in `docker-compose.yml` for further configuration.
|
||||
|
||||
## multi-account setup
|
||||
## multi-account setup with Caddy
|
||||
|
||||
authentication / user routing has been left out of the design of gossa, as simple tools are already available for this purpose. [caddy](https://caddyserver.com/v1/) is used here as an example, but other proxy can be used in a similar fashion.
|
||||
authentication / user routing has been left out of the design of gossa, as simple tools are already available for this purpose. [caddy](https://caddyserver.com) is used here as an example, but other proxy can be used in a similar fashion.
|
||||
|
||||
### example 1 root, multiple read-only users
|
||||
|
||||
this sample caddy config will
|
||||
+ enable https on the domain myserver.com
|
||||
This sample Caddyfile will
|
||||
+ enable https on the domain myserver.com (http will be automatically redirected to https)
|
||||
+ password protect the access
|
||||
+ route the root user requests to 1 gossa instance
|
||||
+ route user1 and user2 requests to a readonly gossa instance
|
||||
|
||||
<details>
|
||||
<summary>Legacy Caddy v1 Caddyfile</summary>
|
||||
|
||||
```sh
|
||||
myserver.com
|
||||
|
||||
# proxy regular and read only instance
|
||||
proxy / 127.0.0.1:8001
|
||||
proxy /ro 127.0.0.1:8002 { without /ro }
|
||||
|
||||
# reroute non-root user to read-only
|
||||
# cm9... is the output of `printf "root:password" | base64`
|
||||
rewrite {
|
||||
if {>Authorization} not "Basic cm9vdDpwYXNzd29yZA=="
|
||||
to /ro/{path}
|
||||
}
|
||||
|
||||
# gate access
|
||||
basicauth / root password
|
||||
basicauth / ro_user1 passworduser1
|
||||
basicauth / ro_user2 passworduser2
|
||||
```
|
||||
</details>
|
||||
|
||||
Caddy v2 Caddyfile
|
||||
|
||||
```sh
|
||||
myserver.com
|
||||
|
||||
# proxy regular and read only instance
|
||||
proxy / 127.0.0.1:8001
|
||||
proxy /ro 127.0.0.1:8002 { without /ro }
|
||||
|
||||
# reroute non-root user to read-only
|
||||
# cm9... is the output of `printf "root:password" | base64`
|
||||
rewrite {
|
||||
if {>Authorization} not "Basic cm9vdDpwYXNzd29yZA=="
|
||||
to /ro/{path}
|
||||
# gate access
|
||||
basic_auth {
|
||||
root $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup"
|
||||
ro_user1 $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup"
|
||||
ro_user2 $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup"
|
||||
}
|
||||
|
||||
# gate access
|
||||
basicauth / root password
|
||||
basicauth / ro_user1 passworduser1
|
||||
basicauth / ro_user2 passworduser2
|
||||
# named matcher for root user
|
||||
@isroot {
|
||||
vars {http.auth.user.id} root
|
||||
}
|
||||
|
||||
# proxy regular and read only instance
|
||||
handle @isroot {
|
||||
reverse_proxy 127.0.0.1:8001
|
||||
}
|
||||
# route non-root user to read only instance
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:8002
|
||||
}
|
||||
```
|
||||
|
||||
then simply start the 2 gossa instances, and caddy
|
||||
@@ -85,30 +116,59 @@ then simply start the 2 gossa instances, and caddy
|
||||
|
||||
### example 2 users on 2 different folders
|
||||
|
||||
this sample caddy config will
|
||||
+ enable https on the domain myserver.com
|
||||
This sample Caddyfile will
|
||||
+ enable https on the domain myserver.com (http will be automatically redirected to https)
|
||||
+ password protect the access
|
||||
+ route user1 to own folder
|
||||
+ route user2 to own folder
|
||||
+ share a folder between 2 users with a symlink
|
||||
|
||||
<details>
|
||||
<summary>Legacy Caddy v1 Caddyfile</summary>
|
||||
|
||||
```sh
|
||||
myserver.com
|
||||
|
||||
proxy /user1 127.0.0.1:8001 { without /user1 }
|
||||
proxy /user2 127.0.0.1:8002 { without /user2 }
|
||||
|
||||
basicauth / user1 passworduser1
|
||||
basicauth / user2 passworduser2
|
||||
|
||||
rewrite {
|
||||
if {>Authorization} is "Basic dXNlcjE6cGFzc3dvcmR1c2VyMQ=="
|
||||
to /user1/{path}
|
||||
}
|
||||
|
||||
rewrite {
|
||||
if {>Authorization} is "Basic dXNlcjI6cGFzc3dvcmR1c2VyMg=="
|
||||
to /user2/{path}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
Caddy v2 Caddyfile
|
||||
|
||||
```sh
|
||||
myserver.com
|
||||
|
||||
proxy /user1 127.0.0.1:8001 { without /user1 }
|
||||
proxy /user2 127.0.0.1:8002 { without /user2 }
|
||||
|
||||
basicauth / user1 passworduser1
|
||||
basicauth / user2 passworduser2
|
||||
|
||||
rewrite {
|
||||
if {>Authorization} is "Basic dXNlcjE6cGFzc3dvcmR1c2VyMQ=="
|
||||
to /user1/{path}
|
||||
basic_auth {
|
||||
user1 $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup"
|
||||
user2 $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup"
|
||||
}
|
||||
|
||||
rewrite {
|
||||
if {>Authorization} is "Basic dXNlcjI6cGFzc3dvcmR1c2VyMg=="
|
||||
to /user2/{path}
|
||||
@user1auth {
|
||||
vars {http.auth.user.id} user1
|
||||
}
|
||||
handle @user1auth {
|
||||
reverse_proxy 127.0.0.1:8001
|
||||
}
|
||||
|
||||
@user2auth {
|
||||
vars {http.auth.user.id} user2
|
||||
}
|
||||
handle @user2auth {
|
||||
reverse_proxy 127.0.0.1:8002
|
||||
}
|
||||
```
|
||||
|
||||
@@ -124,3 +184,12 @@ start 2 gossa instances, and caddy
|
||||
% ./gossa -p 8002 -symlinks=true test/user2 &
|
||||
% ./caddy
|
||||
```
|
||||
|
||||
## nginx setup
|
||||
|
||||
In order to allow for larger uploads, it's recommended to increase the maximum body size on your nginx config :
|
||||
|
||||
```
|
||||
# increase maximum request size
|
||||
client_max_body_size 100M;
|
||||
```
|
||||
|
||||
Vendored
+10
@@ -11,6 +11,7 @@ const rmMsg = () => !confirm('Remove file?\n')
|
||||
const ensureMove = () => !confirm('move items?')
|
||||
const isRo = () => window.ro
|
||||
|
||||
// DOM elements
|
||||
const upBarName = document.getElementById('upBarName')
|
||||
const upBarPc = document.getElementById('upBarPc')
|
||||
const upGrid = document.getElementById('drop-grid')
|
||||
@@ -29,6 +30,8 @@ const editor = document.getElementById('text-editor')
|
||||
const crossIcon = document.getElementById('quitAll')
|
||||
const toast = document.getElementById('toast')
|
||||
const table = document.getElementById('linkTable')
|
||||
const helpMsg = document.getElementById('help_message')
|
||||
|
||||
const transparentPixel = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
|
||||
|
||||
// helpers
|
||||
@@ -847,5 +850,12 @@ function init () {
|
||||
const matchingA = allA.find(a => a.href === cleanURL)
|
||||
padOn(matchingA)
|
||||
}
|
||||
|
||||
// check if we're at root path
|
||||
if (location.pathname === window.extraPath + '/') {
|
||||
helpMsg.style.display = 'block'
|
||||
} else {
|
||||
helpMsg.style.display = 'none'
|
||||
}
|
||||
}
|
||||
init()
|
||||
|
||||
Vendored
+15
-1
@@ -24,6 +24,9 @@
|
||||
.file-size {
|
||||
display: none !important;
|
||||
}
|
||||
#help_message {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* hello standards 👋🏻 */
|
||||
@@ -388,6 +391,18 @@ h1 > span:hover {
|
||||
z-index: 101;
|
||||
}
|
||||
|
||||
#help_message {
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
position: fixed;
|
||||
bottom: 5px;
|
||||
right: 13px;
|
||||
opacity: 50%;
|
||||
user-select: none;
|
||||
display: none;
|
||||
margin: unset;
|
||||
}
|
||||
|
||||
.icon-large-folder {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAJFBMVEUAAACZmQAICAj4+Pj//5nMzGb/+/AAAAD/zJkzMwAAAP////+gebovAAAAAXRSTlMAQObYZgAAAAFiS0dECx/XxMAAAAAHdElNRQfiBhgXAy5jJ3EAAAAA1klEQVQoz42RMQ7CMBAEQ0mZIIp0/AGecJFO7kC2FKXmATxikaxUtPS48Ss5XxwnoWI7j3f3fHJV/aFa1KzOuwsRdacFHCjpthjuChbL0U261ipJnDd65IokJhCaGTCzEOZ25SAwaAUgASoOyFFzxWEsEkQGz2BskFJfHMMwWB07AVAXg/QwZscYPzHqXAVicDE4eJ47QOOoHexzhINxQRxIEVkC6I217OGLg9DLIn7qkHHEWiAvEwAdJyQlNKK3gKfcITuxZjzrO+qt2mr/3uj1z1//6AsSG3mwAQQN9wAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0wNi0yNFQyMzowMzo0Ni0wNDowMFfZ8nYAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMDYtMjRUMjM6MDM6NDYtMDQ6MDAmhErKAAAAAElFTkSuQmCC");
|
||||
}
|
||||
@@ -466,4 +481,3 @@ h1 > span:hover {
|
||||
.icon-dl {
|
||||
background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0yMCAxNVYxOEMyMCAxOS4xMDQ2IDE5LjEwNDYgMjAgMTggMjBINkM0Ljg5NTQzIDIwIDQgMTkuMTA0NiA0IDE4TDQgMTVNOCAxMUwxMiAxNU0xMiAxNUwxNiAxMU0xMiAxNVYzIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4NCjwvc3ZnPg==");
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
@@ -78,6 +78,7 @@
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
<p id="help_message">Help: Ctrl/Cmd + h<p>
|
||||
</body>
|
||||
<div id="upBar" class="bar">
|
||||
<span style="display: none;" class="barName" id="upBarName"></span>
|
||||
|
||||
Reference in New Issue
Block a user