Compare commits

..

No commits in common. "master" and "v1.3.2" have entirely different histories.

39 changed files with 68 additions and 181 deletions

View file

@ -1,6 +0,0 @@
cache
compose.yaml
*.json
LICENSE
*.md
services

1
.gitignore vendored Executable file → Normal file
View file

@ -1,5 +1,4 @@
**/cache **/cache
**/compose.yaml
**/config.json **/config.json
**/skunkyart **/skunkyart
**/skunkyart-* **/skunkyart-*

View file

@ -1,23 +0,0 @@
ARG GO_VERSION=1.18
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS build
ARG TARGETOS
ARG TARGETARCH
WORKDIR /build
COPY . .
RUN CGO_ENABLED=0 GOARCH=${TARGETARCH} GOOS=${TARGETOS} go build -ldflags "-s -w -extldflags '-static'" && \
echo "skunkyart:x:10000:10000:SkunkyArt user:/:/sbin/nologin" > /etc/minimal-passwd && \
echo "skunkyart:x:10000:" > /etc/minimal-group
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /build/static /static
COPY --from=build /build/skunkyart /skunkyart
COPY --from=build /etc/minimal-passwd /etc/passwd
COPY --from=build /etc/minimal-group /etc/group
USER skunkyart
ENTRYPOINT ["/skunkyart"]

8
INSTANCES.md Executable file → Normal file
View file

@ -2,10 +2,8 @@ JSON variant should be used from master — https://git.macaw.me/skunky/SkunkyAr
|Instance|Yggdrasil|I2P|Tor|NSFW|Proxifying|Modified Sources|Country| |Instance|Yggdrasil|I2P|Tor|NSFW|Proxifying|Modified Sources|Country|
|:------:|:-------:|:-:|:-:|:--:|:--------:|:--------------:|:-----:| |:------:|:-------:|:-:|:-:|:--:|:--------:|:--------------:|:-----:|
|[lost-skunk.cc](https://lost-skunk.cc/skunkyart)|[Yes](http://[201:f137:d1ac:920e:cd42:bfd1:1e83:da1d]/skunkyart)|No|No| No | Yes | No | Finland | |[skunky.ebloid.ru](https://skunky.ebloid.ru/art)|[Yes](http://[201:eba5:d1fc:bf7b:cfcb:a811:4b8b:7ea3]/art)|No|No| No | Yes | No | Russia |
|[orehus.club](https://sa.orehus.club)|No|No|No| Yes | No | No | Germany | |[clovius.club](https://skunky.clovius.club)|No|No|No| Yes | Yes | No | Sweden |
|[bloat.cat](https://skunky.bloat.cat)|No|No|No| Yes | Yes | No | Germany | |[bloat.cat](https://skunky.bloat.cat)|No|No|No| Yes | Yes | No | Germany |
|[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | No | Germany | |[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | No | Germany |
|[art.bloat.cat](https://art.bloat.cat)|No|No|No| Yes | Yes | No | Germany | |[art.bloat.cat](https://art.bloat.cat)|No|No|No| Yes | Yes | No | Germany |
|[dc09.ru](https://sa.dc09.ru)|No|No|No| No | Yes | No | Russia |
|[opnxng.com](https://da.opnxng.com)|No|No|No| Yes | Yes | No | Singapore |

0
LICENSE Executable file → Normal file
View file

8
README.md Executable file → Normal file
View file

@ -1,10 +1,6 @@
> [!NOTE]
> Currently, due to school, I cannot actively develop this project :(
> However, this does not mean that development has stopped. Just wait for the summer. For questions, write either to the Matrix room or to me in DM.
<img src="static/images/logo.png" alt="SkunkyArt" title="SkunkyArt Logo" width="20%" loading="lazy"/> <img src="static/images/logo.png" alt="SkunkyArt" title="SkunkyArt Logo" width="20%" loading="lazy"/>
[![Matrix room](https://img.shields.io/badge/matrix-000000?style=for-the-badge&logo=Matrix&logoColor=white)](https://go.kde.org/matrix/#/#skunkyart:gnulinux.club) [![Matrix room](https://img.shields.io/badge/matrix-000000?style=for-the-badge&logo=Matrix&logoColor=white)](https://go.kde.org/matrix/#/#skunkyart:ebloid.ru)
Instances: [`INSTANCES.md`](/skunky/SkunkyArt/src/branch/master/INSTANCES.md) Instances: [`INSTANCES.md`](/skunky/SkunkyArt/src/branch/master/INSTANCES.md)
@ -25,7 +21,6 @@ To do this, you must either make a PR by adding your instance to the `instances.
1. the Instance must not use Cloudflare. 1. the Instance must not use Cloudflare.
2. If your instance has modified source code, you need to publish it to any free platform. For example, Github and Gitlab are not. 2. If your instance has modified source code, you need to publish it to any free platform. For example, Github and Gitlab are not.
## Acknowledgements ## Acknowledgements
* [vlnst](https://git.bloat.cat/vlnst) — wrote a Docker file.
* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) — helped me understand Go and gave me a lot of useful advice on this language. * [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) — helped me understand Go and gave me a lot of useful advice on this language.
* [meoww](https://codeberg.org/meoww) — translated some sentences into English and wrote a service for openrc * [meoww](https://codeberg.org/meoww) — translated some sentences into English and wrote a service for openrc
@ -46,6 +41,5 @@ SkunkyArt 🦨 — альтернативный фронтенд к DeviantArt,
1. Инстанс не должен использовать Cloudflare итп. 1. Инстанс не должен использовать Cloudflare итп.
2. Если ваш инстанс имеет модифицированный исходный код, то вам нужно опубликовать его на любую свободную площадку. Например, Github и Gitlab таковыми не являются. 2. Если ваш инстанс имеет модифицированный исходный код, то вам нужно опубликовать его на любую свободную площадку. Например, Github и Gitlab таковыми не являются.
## Благодарности ## Благодарности
* [vlnst](https://git.bloat.cat/vlnst) — написал Docker-файл.
* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) — помог разобраться в Go и много чего полезного посоветовал по этому языку. * [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) — помог разобраться в Go и много чего полезного посоветовал по этому языку.
* [meoww](https://codeberg.org/meoww) — перевела некоторые предложения на английский язык и написала сервис для openrc * [meoww](https://codeberg.org/meoww) — перевела некоторые предложения на английский язык и написала сервис для openrc

0
REDIRECTS.md Executable file → Normal file
View file

0
SETUP-RU.md Executable file → Normal file
View file

0
SETUP.md Executable file → Normal file
View file

0
TODO.md Executable file → Normal file
View file

8
app/api.go Executable file → Normal file
View file

@ -41,18 +41,12 @@ func (a API) Error(description string, status int) {
func (a API) sendMedia(d *devianter.Deviation) { func (a API) sendMedia(d *devianter.Deviation) {
mediaUrl, name := devianter.UrlFromMedia(d.Media) mediaUrl, name := devianter.UrlFromMedia(d.Media)
a.main.SetFilename(name) a.main.SetFilename(name)
if len(mediaUrl) != 0 {
return
}
if CFG.Proxy { if len(mediaUrl) != 0 {
mediaUrl = mediaUrl[21:] mediaUrl = mediaUrl[21:]
dot := strings.Index(mediaUrl, ".") dot := strings.Index(mediaUrl, ".")
a.main.Writer.Header().Del("Content-Type") a.main.Writer.Header().Del("Content-Type")
a.main.DownloadAndSendMedia(mediaUrl[:dot], mediaUrl[dot+11:]) a.main.DownloadAndSendMedia(mediaUrl[:dot], mediaUrl[dot+11:])
} else {
a.main.Writer.Header().Add("Location", mediaUrl)
a.main.Writer.WriteHeader(302)
} }
} }

72
app/cache.go Executable file → Normal file
View file

@ -38,7 +38,17 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
fileName := sha1.Sum([]byte(subdomain + path)) fileName := sha1.Sum([]byte(subdomain + path))
filePath := CFG.Cache.Path + "/" + hex.EncodeToString(fileName[:]) filePath := CFG.Cache.Path + "/" + hex.EncodeToString(fileName[:])
c := func() { mx.Lock()
if tempFS[fileName] == nil {
tempFS[fileName] = &file{}
}
mx.Unlock()
if tempFS[fileName].Content != nil {
response = tempFS[fileName].Content
tempFS[fileName].Score += 2
break
} else {
file, err := os.Open(filePath) file, err := os.Open(filePath)
if err != nil { if err != nil {
if dwnld := Download(url.String()); dwnld.Status == 200 && dwnld.Headers["Content-Type"][0][:5] == "image" { if dwnld := Download(url.String()); dwnld.Status == 200 && dwnld.Headers["Content-Type"][0][:5] == "image" {
@ -53,44 +63,27 @@ func (s skunkyart) DownloadAndSendMedia(subdomain, path string) {
try(e) try(e)
response = file response = file
} }
}
if CFG.Cache.MemCache { go func() {
mx.Lock() defer restore()
if tempFS[fileName] == nil {
tempFS[fileName] = &file{}
}
mx.Unlock()
if tempFS[fileName].Content != nil { mx.RLock()
response = tempFS[fileName].Content tempFS[fileName].Content = response
tempFS[fileName].Score += 2 mx.RUnlock()
break
} else {
c()
go func() {
defer restore()
mx.RLock() for {
tempFS[fileName].Content = response time.Sleep(1 * time.Minute)
mx.RUnlock()
for { mx.Lock()
time.Sleep(1 * time.Minute) if tempFS[fileName].Score <= 0 {
delete(tempFS, fileName)
mx.Lock()
if tempFS[fileName].Score <= 0 {
delete(tempFS, fileName)
mx.Unlock()
return
}
tempFS[fileName].Score--
mx.Unlock() mx.Unlock()
return
} }
}() tempFS[fileName].Score--
} mx.Unlock()
} else { }
c() }()
} }
case CFG.Proxy: case CFG.Proxy:
dwnld := Download(url.String()) dwnld := Download(url.String())
@ -119,7 +112,6 @@ func InitCacheSystem() {
println(err.Error()) println(err.Error())
} }
var total int64
for _, file := range dir { for _, file := range dir {
fileName := c.Path + "/" + file.Name() fileName := c.Path + "/" + file.Name()
fileInfo, err := file.Info() fileInfo, err := file.Info()
@ -136,15 +128,9 @@ func InitCacheSystem() {
} }
} }
total += fileInfo.Size() if c.MaxSize != 0 && fileInfo.Size() > c.MaxSize {
// if c.MaxSize != 0 && fileInfo.Size() > c.MaxSize { try(os.RemoveAll(fileName))
// try(os.RemoveAll(fileName)) }
// }
}
if c.MaxSize != 0 && total > c.MaxSize {
try(os.RemoveAll(c.Path))
os.Mkdir(c.Path, 0700)
} }
time.Sleep(time.Second * time.Duration(c.UpdateInterval)) time.Sleep(time.Second * time.Duration(c.UpdateInterval))

0
app/cli.go Executable file → Normal file
View file

7
app/config.go Executable file → Normal file
View file

@ -12,13 +12,12 @@ import (
) )
var Release struct { var Release struct {
Version string Version string
Description string Description string
} }
type cache_config struct { type cache_config struct {
Enabled bool Enabled bool
MemCache bool `json:"memcache"`
Path string Path string
MaxSize int64 `json:"max-size"` MaxSize int64 `json:"max-size"`
Lifetime string Lifetime string
@ -94,9 +93,9 @@ func ExecuteConfig() {
About = instanceAbout{ About = instanceAbout{
Proxy: CFG.Proxy, Proxy: CFG.Proxy,
Nsfw: CFG.Nsfw, Nsfw: CFG.Nsfw,
} }
static.StaticPath = CFG.StaticPath static.StaticPath = CFG.StaticPath
devianter.UserAgent = CFG.UserAgent devianter.UserAgent = CFG.UserAgent
} }

2
app/parsers.go Executable file → Normal file
View file

@ -46,7 +46,7 @@ func (s skunkyart) ParseComments(c devianter.Comments, daError devianter.Error)
if x.Parent > 0 { if x.Parent > 0 {
cmmts.WriteString(` In reply to <a href="`) cmmts.WriteString(` In reply to <a href="`)
cmmts.WriteString(s._pth) cmmts.WriteString(Path)
cmmts.WriteString("#") cmmts.WriteString("#")
cmmts.WriteString(strconv.Itoa(x.Parent)) cmmts.WriteString(strconv.Itoa(x.Parent))
cmmts.WriteString(`">`) cmmts.WriteString(`">`)

7
app/router.go Executable file → Normal file
View file

@ -9,7 +9,7 @@ import (
"strings" "strings"
) )
var Host string var Host, Path string
func Router() { func Router() {
parsepath := func(path string) map[int]string { parsepath := func(path string) map[int]string {
@ -54,14 +54,15 @@ func Router() {
// функция, что управляет всем // функция, что управляет всем
handle := func(w http.ResponseWriter, r *http.Request) { handle := func(w http.ResponseWriter, r *http.Request) {
path := parsepath(r.URL.Path) Path = r.URL.Path
path := parsepath(Path)
Host = "http://" + r.Host Host = "http://" + r.Host
if h := r.Header["X-Forwarded-Proto"]; len(h) != 0 && h[0] == "https" { if h := r.Header["X-Forwarded-Proto"]; len(h) != 0 && h[0] == "https" {
Host = "https://" + r.Host Host = "https://" + r.Host
} }
var skunky = skunkyart{Version: Release.Version} var skunky = skunkyart{Version: Release.Version}
skunky._pth = r.URL.Path
skunky.Args = r.URL.Query() skunky.Args = r.URL.Query()
arg := skunky.Args.Get arg := skunky.Args.Get

0
app/stat-freebsd.go Executable file → Normal file
View file

0
app/stat.go Executable file → Normal file
View file

3
app/util.go Executable file → Normal file
View file

@ -63,7 +63,6 @@ type instanceAbout struct {
type skunkyart struct { type skunkyart struct {
Writer http.ResponseWriter Writer http.ResponseWriter
_pth string
Args url.Values Args url.Values
Page int Page int
@ -275,7 +274,7 @@ func (s skunkyart) NavBase(c DeviationList) string {
prevrev := func(msg string, page int, onpage bool) { prevrev := func(msg string, page int, onpage bool) {
if !onpage { if !onpage {
list.WriteString(`<a href="`) list.WriteString(`<a href="`)
list.WriteString(s._pth) list.WriteString(Path)
list.WriteString(`?p=`) list.WriteString(`?p=`)
list.WriteString(strconv.Itoa(page)) list.WriteString(strconv.Itoa(page))
if s.Type != 0 { if s.Type != 0 {

13
app/wrapper.go Executable file → Normal file
View file

@ -36,12 +36,13 @@ func (s skunkyart) GRUser() {
for _, x := range g.Gruser.Page.Modules { for _, x := range g.Gruser.Page.Modules {
switch x.Name { switch x.Name {
case "about", "group_about": case "about", "group_about":
if g.Owner.Group { switch g.Owner.Group {
case true:
var about = &x.ModuleData.GroupAbout var about = &x.ModuleData.GroupAbout
group.Group = true group.Group = true
group.CreationDate = x.ModuleData.GroupAbout.FoundatedAt.UTC().String() group.CreationDate = x.ModuleData.GroupAbout.FoundatedAt.UTC().String()
group.About.DescriptionFormatted = ParseDescription(about.Description) group.About.DescriptionFormatted = ParseDescription(about.Description)
} else if false { case false:
group.About.A = x.ModuleData.About group.About.A = x.ModuleData.About
var about = &group.About.A var about = &group.About.A
group.CreationDate = time.Unix(time.Now().Unix()-x.ModuleData.About.RegDate, 0).UTC().String() group.CreationDate = time.Unix(time.Now().Unix()-x.ModuleData.About.RegDate, 0).UTC().String()
@ -185,14 +186,6 @@ func (s skunkyart) Deviation(author, postname string) {
return return
} }
if post.Post.Deviation.NSFW && !CFG.Nsfw {
s.Writer.WriteHeader(403)
wr(s.Writer, `<html><link rel="stylesheet" href="`+
UrlBuilder("stylesheet")+
`" /><h1>NSFW content are disabled on this instance.</h1></html>`)
return
}
if post.Post.Comments.Total <= 50 { if post.Post.Comments.Total <= 50 {
post.Post.Comments.Cursor = "" post.Post.Comments.Cursor = ""
} }

View file

@ -1,12 +0,0 @@
services:
skunkyart:
container_name: skunkyart
restart: unless-stopped
build: .
ports:
- "127.0.0.1:3003:3003"
security_opt:
- no-new-privileges:true
volumes:
- ./config.json:/config.json:ro
- ./cache:/cache # Ensure cache folder has a 10000:10000 ownership.

3
config.example.json Executable file → Normal file
View file

@ -6,12 +6,11 @@
"path": "cache", "path": "cache",
"lifetime": null, "lifetime": null,
"max-size": 200, "max-size": 200,
"memcache": false,
"update-interval": 5 "update-interval": 5
}, },
"static-path": "static", "static-path": "static",
"download-proxy": "http://127.0.0.1:8080", "download-proxy": "http://127.0.0.1:8080",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
"proxy": true, "proxy": true,
"nsfw": false "nsfw": true
} }

0
go.mod Executable file → Normal file
View file

0
go.sum Executable file → Normal file
View file

40
instances.json Executable file → Normal file
View file

@ -1,11 +1,11 @@
{ {
"instances": [ "instances": [
{ {
"title": "lost-skunk.cc", "title": "skunky.ebloid.ru",
"country": "Finland", "country": "Russia",
"urls": { "urls": {
"ygg": "http://[201:f137:d1ac:920e:cd42:bfd1:1e83:da1d]/skunkyart", "ygg": "http://[201:eba5:d1fc:bf7b:cfcb:a811:4b8b:7ea3]/art",
"clearnet": "https://lost-skunk.cc/skunkyart" "clearnet": "https://skunky.ebloid.ru/art"
}, },
"settings": { "settings": {
"proxy": true, "proxy": true,
@ -13,13 +13,13 @@
} }
}, },
{ {
"title": "orehus.club", "title": "clovius.club",
"country": "Germany", "country": "Sweden",
"urls": { "urls": {
"clearnet": "https://sa.orehus.club" "clearnet": "https://skunky.clovius.club"
}, },
"settings": { "settings": {
"proxy": false, "proxy": true,
"nsfw": true "nsfw": true
} }
}, },
@ -55,28 +55,6 @@
"proxy": true, "proxy": true,
"nsfw": true "nsfw": true
} }
},
{
"title": "dc09.ru",
"country": "Russia",
"urls": {
"clearnet": "https://sa.dc09.ru"
},
"settings": {
"proxy": true,
"nsfw": false
}
},
{
"title": "opnxng.com",
"country": "Singapore",
"urls": {
"clearnet": "https://da.opnxng.com"
},
"settings": {
"proxy": true,
"nsfw": true
}
} }
] ]
} }

0
main.go Executable file → Normal file
View file

0
services/skunkyart.example.service Executable file → Normal file
View file

30
static/css/skunky.css Executable file → Normal file
View file

@ -1,6 +1,6 @@
/* TAGS */ /* TAGS */
html { html {
font-family: ubuntu, system-ui; font-family: Ubuntu;
background-color:black; background-color:black;
color: rgb(234, 216, 216); color: rgb(234, 216, 216);
} }
@ -45,22 +45,24 @@ input:focus {
justify-content: center; justify-content: center;
} }
.block { .block {
padding: 0px 0px 6px 0px; max-width: 20%;
border: 3px solid #000; height: 0%;
padding: 4px;
border-radius: 2px;
border: 3px solid #091f19;
word-break: break-all; word-break: break-all;
background-color: #091f19; background-color: #091f19;
margin-left: 5px; margin-left: 5px;
margin-top: 5px; margin-top: 5px;
text-align: center; text-align: center;
} }
.block h1 {
padding: 8.5vh;
}
.block:hover { .block:hover {
border: 3px solid #4d27d6; border: 3px solid #4d27d6;
transition: 400ms; transition: 400ms;
} }
.block img, .plates .user-plate img {
width: 100%;
}
.block p { .block p {
word-break: break-all; word-break: break-all;
} }
@ -183,20 +185,6 @@ input:focus {
font-size: 60%; font-size: 60%;
max-width: 80% max-width: 80%
} }
.block img, .plates .user-plate img {
width: 100%;
}
}
@media (orientation: landscape) {
.block {
width: 20%;
}
.block img, .plates .user-plate img {
width: 100%;
height: 30vh;
object-fit: cover;
}
} }
@media (max-width: 1462px) and (orientation: landscape) { @media (max-width: 1462px) and (orientation: landscape) {

4
static/html/about.htm Executable file → Normal file
View file

@ -6,7 +6,7 @@
<p> <p>
SkunkyArt is an alternative frontend for deviantart.com, written in Go. SkunkyArt is an alternative frontend for deviantart.com, written in Go.
</p> </p>
<h3><a href="https://go.kde.org/matrix/#/#skunkyart:gnulinux.club" target="_blank">Room in [matrix]</a></h3> <h3><a href="https://go.kde.org/matrix/#/#skunkyart:ebloid.ru" target="_blank">Room in [matrix]</a></h3>
<b>Instance settings:</b> <b>Instance settings:</b>
<ul> <ul>
<li><b>NSFW</b>: <span class="about-{{.Templates.About.Nsfw}}">{{if .Templates.About.Nsfw}}YES{{else}}NO{{end}}</span></li> <li><b>NSFW</b>: <span class="about-{{.Templates.About.Nsfw}}">{{if .Templates.About.Nsfw}}YES{{else}}NO{{end}}</span></li>
@ -44,6 +44,6 @@
{{end}} {{end}}
</ul> </ul>
</details> </details>
<p>Copyright <a href="https://go.kde.org/matrix/#/@ls:gnulinux.club" target="_blank">lost+skunk</a>, X11. <a href="https://git.macaw.me/skunky/skunkyart/src/tag/v{{.Version}}" target="_blank">SkunkyArt v{{.Version}}</a></p> <p>Copyright <a href="https://go.kde.org/matrix/#/@softpigeones:ebloid.ru" target="_blank">lost+skunk</a>, X11. <a href="https://git.macaw.me/skunky/skunkyart/src/tag/v{{.Version}}" target="_blank">SkunkyArt v{{.Version}}</a></p>
</main> </main>
</html> </html>

0
static/html/daily.htm Executable file → Normal file
View file

0
static/html/deviantion.htm Executable file → Normal file
View file

2
static/html/gruser.htm Executable file → Normal file
View file

@ -30,7 +30,7 @@
</form> <h1>| {{.Templates.GroupUser.GR.Owner.Username}}</h1> </form> <h1>| {{.Templates.GroupUser.GR.Owner.Username}}</h1>
</header> </header>
{{if eq .Type 'a'}} {{if eq .Type 'a'}}
{{if and (and (ne .Templates.About.Nsfw true) (ne .Templates.GroupUser.About.BGMeta.NSFW true)) (ne .Templates.GroupUser.About.BG "")}} {{if ne .Templates.GroupUser.About.BG ""}}
<a href="{{.Templates.GroupUser.About.BGMeta.Url}}" class="ubg"><img title="{{if ne .Templates.GroupUser.GR.Owner.Username .Templates.GroupUser.About.BGMeta.Author.Username}} <a href="{{.Templates.GroupUser.About.BGMeta.Url}}" class="ubg"><img title="{{if ne .Templates.GroupUser.GR.Owner.Username .Templates.GroupUser.About.BGMeta.Author.Username}}
{{.Templates.GroupUser.About.BGMeta.Author.Username}} - {{end}}{{.Templates.GroupUser.About.BGMeta.Title}}" src="{{.Templates.GroupUser.About.BG}}"></a> {{.Templates.GroupUser.About.BGMeta.Author.Username}} - {{end}}{{.Templates.GroupUser.About.BGMeta.Title}}" src="{{.Templates.GroupUser.About.BG}}"></a>
{{end}} {{end}}

0
static/html/head.htm Executable file → Normal file
View file

0
static/html/header.htm Executable file → Normal file
View file

0
static/html/index.htm Executable file → Normal file
View file

0
static/html/search.htm Executable file → Normal file
View file

0
static/images/logo.png Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 398 KiB

After

Width:  |  Height:  |  Size: 398 KiB

Before After
Before After

0
static/templates-noembed.go Executable file → Normal file
View file

0
static/templates.go Executable file → Normal file
View file