SVNからのミラー

This commit is contained in:
2026-01-21 03:36:58 +09:00
commit 965aae7f57
26 changed files with 1101 additions and 0 deletions

45
CHANGELOG.md Normal file
View File

@@ -0,0 +1,45 @@
# 2.3.0
* GPL→ISC
* GNU Make→BSD Make
* ヘルプとバージョン表示CLIの削除
# 2.2.0
* ポート番号の修正
* 言語はliblocale化
* 複数言語対応
# 2.1.1
* バグを修正
# 2.1.0
* ローカライズは関数化
* API機能性
* サーバーのソースコードを短くに
# 2.0.2
* Makefileでの「make install」部分を修正
* manページで「オプションなし」部分を追加
* 「短熟」→「短縮」を修正
* クッキーは無効にする又はクッキーが未対応のブラウザ対応の修正
# 2.0.1
* 送信ページの無英訳を修正
* URLは500文字移行のエラー未表示を修正
* ページデータの値を改良
* ページでバージョンの表示を追加
# 2.0.0
* Makefile化
* ソースコードは複数ファイルに分ける
* コマンドラインからURL短縮
* バージョンの表示
* ヘルプの表示
* manページ
* ポート番号はご自由に決める様にしたデフォルトは9910
# 1.0.0
* PHPからGoに交換しました
* 今度からバージョンを付きます
# それ以前
* 色々

13
LICENSE.txt Normal file
View File

@@ -0,0 +1,13 @@
Copyright © 2018-2024 by 076.moe
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

90
Makefile Normal file
View File

@@ -0,0 +1,90 @@
UNAME_S != uname -s
NAME != cat main.go | grep "var sofname" | awk '{print $$4}' | sed "s/\"//g"
VERSION != cat main.go | grep "var version" | awk '{print $$4}' | sed "s/\"//g"
PREFIX = /usr/local
.if ${UNAME_S} == "Linux"
PREFIX = /usr
.endif
MANPREFIX = ${PREFIX}/share/man
.if ${UNAME_S} == "OpenBSD"
MANPREFIX = ${PREFIX}/man
.endif
CNFPREFIX=/etc
.if ${UNAME_S} == "FreeBSD" || ${UNAME_S} == "NetBSD" || ${UNAME_S} == "Dragonfly"
CNFPREFIX=${PREFIX}/etc
.endif
CC=CGO_ENABLED=0 go build
RELEASE=-ldflags="-s -w" -buildvcs=false
all:
${CC} ${RELEASE} -o ${NAME}
release:
mkdir -p release/bin
env GOOS=linux GOARCH=amd64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-linux-amd64
env GOOS=linux GOARCH=arm64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-linux-arm64
env GOOS=linux GOARCH=riscv64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-linux-riscv64
env GOOS=linux GOARCH=ppc64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-linux-ppc64
env GOOS=linux GOARCH=mips64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-linux-mips64
env GOOS=openbsd GOARCH=amd64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-openbsd-amd64
env GOOS=openbsd GOARCH=arm64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-openbsd-arm64
env GOOS=openbsd GOARCH=mips64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-openbsd-mips64
env GOOS=openbsd GOARCH=ppc64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-openbsd-ppc64
env GOOS=openbsd GOARCH=riscv64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-openbsd-riscv64
env GOOS=openbsd GOARCH=sparc64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-openbsd-sparc64
env GOOS=freebsd GOARCH=amd64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-freebsd-amd64
env GOOS=freebsd GOARCH=arm64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-freebsd-arm64
env GOOS=freebsd GOARCH=riscv64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-freebsd-riscv64
env GOOS=netbsd GOARCH=amd64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-netbsd-amd64
env GOOS=netbsd GOARCH=arm64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-netbsd-arm64
env GOOS=illumos GOARCH=amd64 ${CC} ${RELEASE} -o \
release/bin/${NAME}-${VERSION}-illumos-amd64
clean:
rm -f ${NAME} ${NAME}-${VERSION}.tar.gz
dist: clean
mkdir -p ${NAME}-${VERSION} release/src
cp -R LICENSE.txt Makefile README.md CHANGELOG.md\
view static logo.jpg\
${NAME}.1 *.go *.json go.mod go.sum ${NAME}-${VERSION}
tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}
rm -rf ${NAME}-${VERSION}
install: all
mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
mkdir -p ${DESTDIR}${MANPREFIX}/man1
sed "s/VERSION/${VERSION}/g" < ${NAME}.1 > ${DESTDIR}${MANPREFIX}/man1/${NAME}.1
chmod 644 ${DESTDIR}${MANPREFIX}/man1/${NAME}.1
mkdir -p ${DESTDIR}${CNFPREFIX}/${NAME}
chmod 755 ${DESTDIR}${CNFPREFIX}/${NAME}
uninstall:
rm -f ${DESTDIOR}${PREFIX}/bin/${NAME}\
${DESTDIR}${MANPREFIX}/man1/${NAME}.1\
${DESTDIR}${CNFPREFIX}/${NAME}
.PHONY: all release clean dist install uninstall

282
README.md Normal file
View File

@@ -0,0 +1,282 @@
# URLロリ
クッソ小さいURL短縮作成ソフトだわ〜♡
## インストールする方法
### 従属ソフト
* Go 1.19以上
* nginx又はOpenBSDのrelayd
* 良いOS (GNU/Linux、OpenBSD、又はFreeBSD)
## インストールする方法
### 全部opendoasを使わなければ、sudoをご利用、又はopendoasをインストールして下さい
```sh
make
doas make install
```
### OpenBSD
```sh
nvim /etc/rc.d/urloli
```
```
#!/bin/ksh
daemon="/usr/local/bin/urloli -s"
. /etc/rc.d/rc.subr
rc_bg=YES
rc_reload=NO
rc_cmd $1
```
```sh
chmod +x /etc/rc.d/urloli
rcctl enable urloli
rcctl start urloli
```
### FreeBSD
```sh
nvim /usr/local/etc/rc.d/urloli
```
```
#!/bin/sh
# PROVIDE: urloli
# REQUIRE: NETWORKING SYSLOG
# KEYWORD: shutdown
#
# Add the following lines to /etc/rc.conf to enable urloli:
#
#urloli_enable="YES"
. /etc/rc.subr
name="urloli"
rcvar="urloli_enable"
load_rc_config $name
: ${urloli_enable:="NO"}
: ${urloli_facility:="daemon"}
: ${urloli_priority:="debug"}
command="/usr/local/bin/${name}"
procname="/usr/local/bin/${name}"
pidfile="/var/run/${name}.pid"
start_cmd="${name}_start"
urloli_start() {
for d in /var/db/urloli /var/log/urloli; do
if [ ! -e "$d" ]; then
mkdir "$d"
fi
done
/usr/sbin/daemon -S -l ${urloli_facility} -s ${urloli_priority} -T ${name} \
-p ${pidfile} \
/usr/bin/env -i \
"PATH=/usr/local/bin:${PATH}" \
$command
}
run_rc_command "$1"
```
```sh
sysrc urloli_enable=YES
service start urloli
```
### Crux
```sh
nvim /etc/rc.d/urloli
```
```
#!/bin/sh
#
# /etc/rc.d/urloli: start/stop the urloli daemon
#
SSD=/sbin/start-stop-daemon
NAME=urloli
PROG=/usr/bin/$NAME
PIOD=/run/$NAME.pid
case $1 in
start)
$SSD --start --pidfile $PID --exec $PROG
;;
stop)
$SSD --stop --retry 10 --pidfile $PID
;;
restart)
$0 stop
$0 start
;;
status)
$SSD --status --pidfile $PID
case $? in
0) echo "$PROG は実行中。pid $(cat $PID)" ;;
1) echo "$PROG は実行していませんが、pidファイルは「 $PID 」として存在しそう" ;;
3) echo "$PROG は停止中" ;;
4) echo "状況不明" ;;
esac
;;
*)
echo "usage: $0 [start|sto@|restart|status]"
;;
esac
# End of file
```
### Devuan/Debian/Ubuntu/Arch/Artix/AlmaLinux等
```sh
nvim /etc/init.d/urloli
```
```
#!/bin/sh
#
# chkconfig: 35 90 12
# description: URL Loli server
#
NAME=urloli
DESC=urloli
DAEMON=/usr/bin/$NAME
start () {
echo "URLロリサーバーは開始中\n"
/usr/bin/urloli -s 9910 &>/dev/null &
touch /var/lock/subsys/urloli
echo
}
stop () {
echo "URLロリサーバーは終了中\n"
pkill urloli
rm -f /var/lock/subsys/urloli
echo
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
restart|reload|condrestart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
exit 1
esac
```
## ウェブサーバー
### OpenBSD
```sh
nvim /etc/relayd.conf
```
```
# $OpenBSD: relayd.conf,v 1.5 2018/05/06 20:56:55 benno Exp $
#
relayd_address="0.0.0.0"
table <urloli> { 127.0.0.1 }
http protocol reverse_proxy {
tls keypair "DOMAIN"
match request header append "X-Forwarded-For" value "$REMOTE_ADDR"
match request header append "X-Forwarded-Port" value "$REMOTE_PORT"
match response header set "Referrer-Policy" value "same-origin"
match response header set "X-Frame-Options" value "deny"
match response header set "X-XSS-Protection" value "1; mode=block"
match response header set "X-Content-Type-Options" value "nosniff"
match response header set "Strict-Transport-Security" value "max-age=31536000; includeSubDomains; preload"
match response header set "Cache-Control" value "max-age=86400"
pass request quick header "Host" value "urlo.li" forward to <urloli>
return error
pass
}
relay www {
listen on $relayd_address port 443 tls
protocol $relayd_address
forward to <urloli> check tcp port 9910
}
```
### その他
```sh
server {
server_name DOMAIN www.DOMAIN;
root /var/www/htdocs/urloli;
access_log off;
error_log off;
if ($host = www.DOMAIN) {
return 301 https://DOMAIN$request_uri;
}
location /static {
try_files $uri $uri/ /static/$args;
}
location / {
proxy_pass http://localhost:9910;
}
listen [::]:443 ssl ipv6only=on;
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/DOMAIN/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf
}
server {
if ($host = DOMAIN) {
return 301 https://DOMAIN$request_uri;
}
if ($host = www.DOMAIN) {
return 301 https://DOMAIN$request_uri;
}
listen 80;
listen [::]:80;
server_name DOMAIN www.DOMAIN;
return 404;
}
```

16
checkers.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"strings"
"unicode/utf8"
)
// http://かhttps://で始まるかどうか
func checkprefix (url string) bool {
return strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://")
}
// URLは500文字以内かどうか
func checkcharlim (url string) bool {
return utf8.RuneCountInString(url) <= 500
}

97
config.go Normal file
View File

@@ -0,0 +1,97 @@
package main
import (
"fmt"
"encoding/json"
"io/ioutil"
"runtime"
"os"
"errors"
)
type Config struct {
configpath, linkpath, webpath, domain, ip string
}
func geturl (url string, linkpath string, checkjson bool) (string, string) {
payload := getlinks(linkpath)
for k := range payload {
if checkjson {
if url == payload[k] {
return url, k
}
} else {
if url == k {
return payload[k].(string), k
}
}
}
return "", ""
}
func getlinks (linkpath string) map[string]interface{} {
data, err := ioutil.ReadFile(linkpath)
if err != nil {
fmt.Println("links.jsonを開けられません: ", err)
}
var payload map[string]interface{}
json.Unmarshal(data, &payload)
return payload
}
func getconf () (Config, error) {
var cnf Config
prefix := "/usr"
if runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" {
prefix += "/local"
} else if runtime.GOOS == "netbsd" {
prefix += "/pkg"
}
cnf.configpath = "/etc/urloli/config.json"
cnf.linkpath = "/etc/urloli/links.json"
if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" {
cnf.configpath = prefix + cnf.configpath
cnf.linkpath = prefix + cnf.linkpath
}
data, err := ioutil.ReadFile(cnf.configpath)
if err != nil {
fmt.Println("config.jsonを開けられません: ", err)
return cnf, errors.New(
"コンフィグファイルは " + cnf.configpath + " に創作して下さい。",
)
}
var payload map[string]interface{}
json.Unmarshal(data, &payload)
if payload["webpath"] == nil {
return cnf, errors.New("「webpath」の値が設置していません。")
}
if payload["domain"] == nil {
return cnf, errors.New("「domain」の値が設置していません。")
}
if payload["ip"] == nil {
return cnf, errors.New("「ip」の値が設置していません。")
}
if _, err := os.Stat(payload["webpath"].(string)); err != nil {
fmt.Printf("%v\n", err)
return cnf, errors.New("mkdirコマンドを使って、 " + payload["webpath"].(string))
}
if !checkprefix(payload["domain"].(string)) {
return cnf, errors.New(
"URLは「http://」又は「https://」で始める様にして下さい。",
)
}
cnf.webpath = payload["webpath"].(string)
cnf.domain = payload["domain"].(string)
cnf.ip = payload["ip"].(string)
payload = nil
return cnf, nil
}

5
config.json Normal file
View File

@@ -0,0 +1,5 @@
{
"domain": "https://urlo.li",
"webpath": "/var/www/htdocs/urloli",
"ip": "0.0.0.0"
}

5
go.mod Normal file
View File

@@ -0,0 +1,5 @@
module 076/urloli
go 1.18
require gitler.moe/suwako/goliblocale v1.0.0

2
go.sum Normal file
View File

@@ -0,0 +1,2 @@
gitler.moe/suwako/goliblocale v1.0.0 h1:QiQKNzdgpavwmAaYhAb5pth0I6qS8IJ7q2hYAgpXacU=
gitler.moe/suwako/goliblocale v1.0.0/go.mod h1:pdv9Go5taevY8ClBOA+oLXjGap7G1RmIVKUMF8HSJmU=

4
links.sample.json Normal file
View File

@@ -0,0 +1,4 @@
{
"nuqvx": "https://technicalsuwako.moe",
"076": "https://076.moe"
}

16
locale/en.json Normal file
View File

@@ -0,0 +1,16 @@
{
"top": "Top",
"logo": "Logo",
"fuseiurl": "Invalid URL",
"tansyukuzumi": "Shortened",
"mikensyutu": "Not found",
"errfusei": "The URL should start with \"http://\" or \"https://\".",
"errcharlim": "The URL should be less than 500 characters.",
"errurlent": "Please enter a URL.",
"errurlnai": "This URL could not be found.",
"totop": "Return to toppage",
"canunderaccess": "Please enter a URL.",
"enterurl": "Please enter a URL.",
"submit": "Submit",
"langchange": "Change language"
}

16
locale/ja.json Normal file
View File

@@ -0,0 +1,16 @@
{
"top": "トップ",
"logo": "ロゴ",
"fuseiurl": "不正なURL",
"tansyukuzumi": "短縮済み",
"mikensyutu": "未検出",
"errfusei": "URLは「http://」又は「https://」で始めます。",
"errcharlim": "URLは500文字以内です。",
"errurlent": "URLをご入力下さい。",
"errurlnai": "このURLを見つけられませんでした。",
"totop": "トップページに戻る",
"canunderaccess": "下記のURLからアクセス出来ます。",
"enterurl": "URLをご入力下さい",
"submit": "送信",
"langchange": "言語変更"
}

BIN
logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

53
main.go Normal file
View File

@@ -0,0 +1,53 @@
package main
import (
"fmt"
"os"
"strconv"
)
var sofname = "urloli"
var version = "2.3.0"
func usage() {
fmt.Printf("%s-%s\nusage: %s [-s port] [url]\n", sofname, version, sofname)
}
func main() {
cnf, err := getconf()
if err != nil {
fmt.Println(err)
return
}
args := os.Args
if len(args) < 2 {
usage()
return
}
if len(args) == 2 && args[1] == "-s" {
serv(cnf, 9910)
} else if len(args) == 2 && args[1] != "-s" {
if !checkprefix(args[1]) {
fmt.Println("URLは不正です。終了…")
return
}
_, key := geturl(args[1], cnf.linkpath, true)
if (key != "") {
fmt.Println(cnf.domain + "/" + key)
} else {
fmt.Println(cnf.domain + "/" + insertjson(args[1], cnf.linkpath))
}
return
} else if len(args) == 3 && args[1] == "-s" {
port, err := strconv.Atoi(args[2])
if err != nil {
fmt.Printf("%qは数字ではありません。\n", args[2])
return
}
serv(cnf, port)
}
}

39
makers.go Normal file
View File

@@ -0,0 +1,39 @@
package main
import (
"crypto/rand"
"encoding/json"
"io/ioutil"
"os"
)
func mkstring () string {
stringchars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
newstring := ""
b := make([]byte, 5)
// 乱数を生成
if _, err := rand.Read(b); err != nil {
return "不明なエラー"
}
// ランダムに取り出して文字列を生成
for _, v := range b {
// index が stringchars の長さに収まるように調整
newstring += string(stringchars[int(v)%len(stringchars)])
}
return newstring
}
func insertjson (url string, linkpath string) string {
payload := getlinks(linkpath)
newstring := mkstring()
payload[newstring] = url
m, _ := json.Marshal(&payload)
payload = nil
ioutil.WriteFile(linkpath, m, os.ModePerm)
return newstring
}

236
srv.go Normal file
View File

@@ -0,0 +1,236 @@
package main
import (
"text/template"
"fmt"
"net/http"
"encoding/json"
"strings"
"log"
"os"
"path/filepath"
"gitler.moe/suwako/goliblocale"
)
type (
Page struct {
Tit, Err, Url, Dom, Lan, Ver, Ves string
i18n map[string]string
}
Api struct {
Cod int `json:"code"`
Err string `json:"error"`
Url string `json:"url"`
Mot string `json:"origin"`
New bool `json:"isnew"`
}
Stat struct {
Url string `json:"url"`
Ver string `json:"version"`
}
)
func (p Page) T(key string) string {
return p.i18n[key]
}
func initloc(r *http.Request) string {
supportLang := map[string]bool{
"ja": true,
"en": true,
}
cookie, err := r.Cookie("lang")
if err != nil {
return "ja"
}
if _, ok := supportLang[cookie.Value]; ok {
return cookie.Value
} else {
return "ja"
}
}
func serv(cnf Config, port int) {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
log.Fatal(err)
}
err = os.Chdir(dir)
if err != nil {
log.Fatal(err)
}
http.Handle(
"/static/",
http.StripPrefix("/static/", http.FileServer(http.Dir(cnf.webpath + "/static"))),
)
ftmpl := []string{
cnf.webpath + "/view/index.html",
cnf.webpath + "/view/header.html",
cnf.webpath + "/view/footer.html",
}
http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(200)
buf, _ := json.MarshalIndent(&Stat{Url: cnf.domain, Ver: version}, "", " ")
_, _ = w.Write(buf)
})
http.HandleFunc("/api/lolify", func(w http.ResponseWriter, r *http.Request) {
lang := initloc(r)
i18n, err := goliblocale.GetLocale(cnf.webpath + "/locale/" + lang)
if err != nil {
fmt.Printf("liblocaleエラー%v", err)
return
}
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(200)
res := &Api{Cod: 500, Err: "未対応"}
if r.Method == "POST" {
err := r.ParseForm()
if err != nil {
fmt.Println(err)
res.Err = "失敗"
return
}
if r.PostForm.Get("url") != "" {
addurl := r.PostForm.Get("url")
chkprx := checkprefix(addurl)
chklim := checkcharlim(addurl)
if !chkprx {
res = &Api{Cod: 400, Err: i18n["errfusei"]}
buf, _ := json.MarshalIndent(res, "", " ")
_, _ = w.Write(buf)
return
}
if !chklim {
res = &Api{Cod: 400, Err: i18n["errcharlim"]}
buf, _ := json.MarshalIndent(res, "", " ")
_, _ = w.Write(buf)
return
}
if !chklim || !chkprx {
res = &Api{Cod: 400, Err: i18n["errurlent"]}
buf, _ := json.MarshalIndent(res, "", " ")
_, _ = w.Write(buf)
return
}
chkfn, key := geturl(addurl, cnf.linkpath, true)
if chkfn != "" {
res = &Api{
Cod: 200,
Url: cnf.domain + "/" + key,
Mot: addurl,
New: false,
}
} else {
res = &Api{
Cod: 200,
Url: cnf.domain + "/" + insertjson(addurl, cnf.linkpath),
Mot: addurl,
New: true,
}
}
}
}
buf, _ := json.MarshalIndent(res, "", " ")
_, _ = w.Write(buf)
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
data := &Page{Ver: version, Ves: strings.ReplaceAll(version, ".", "")}
uri := r.URL.Path
lang := initloc(r)
i18n, err := goliblocale.GetLocale(cnf.webpath + "/locale/" + lang)
if err != nil {
fmt.Printf("liblocaleエラー%v", err)
return
}
data.i18n = i18n
data.Lan = lang
// デフォルトページ=未検出
data.Tit = i18n["mikensyutu"]
data.Err = i18n["errurlnai"]
ftmpl[0] = cnf.webpath + "/view/404.html"
tmpl := template.Must(template.ParseFiles(ftmpl[0], ftmpl[1], ftmpl[2]))
if r.Method == "POST" {
err := r.ParseForm()
if err != nil { fmt.Println(err) }
if r.PostForm.Get("sosin") != "" {
if r.PostForm.Get("newadd") == "" {
data.Err = i18n["errurlent"]
return
}
addurl := r.PostForm.Get("newadd")
chkprx := checkprefix(addurl)
chklim := checkcharlim(addurl)
if !chkprx {
data.Tit = i18n["fuseiurl"]
data.Err = i18n["errfusei"]
return
}
if !chklim {
data.Tit = i18n["fuseiurl"]
data.Err = i18n["errcharlim"]
return
}
chkfn, _ := geturl(addurl, cnf.linkpath, true)
if chkfn != "" {
http.Redirect(w, r, addurl, http.StatusSeeOther)
return
}
data.Url = insertjson(addurl, cnf.linkpath)
data.Dom = cnf.domain
data.Tit = i18n["tansyukuzumi"]
ftmpl[0] = cnf.webpath + "/view/submitted.html"
} else if r.PostForm.Get("langchange") != "" {
lang := r.PostForm.Get("lang")
http.SetCookie(
w,
&http.Cookie{Name: "lang", Value: lang, MaxAge: 31536000, Path: "/"},
)
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
} else { // r.Method == "GET"
if uri == "/" {
data.Tit = i18n["top"]
ftmpl[0] = cnf.webpath + "/view/index.html"
} else {
red, _ := geturl(uri[1:], cnf.linkpath, false)
if red != "" {
http.Redirect(w, r, red, http.StatusSeeOther)
return
}
}
} // r.Method
tmpl = template.Must(template.ParseFiles(ftmpl[0], ftmpl[1], ftmpl[2]))
tmpl.Execute(w, data)
data = nil
})
fmt.Println(fmt.Sprint(
"http://" + cnf.ip + ":",
port,
" でサーバーを実行中。終了するには、CTRL+Cを押して下さい。",
))
http.ListenAndServe(fmt.Sprint(cnf.ip + ":", port), nil)
}

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/git.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
static/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

80
static/style.css Normal file
View File

@@ -0,0 +1,80 @@
body {
background-color: #232629;
color: #fcfcfc;
}
a {
color: #ea81e8;
text-decoration: none;
}
hr {
border-color: #dd00dd;
box-shadow: 2px 2px 10px #ff00ff;
width: 100%;
}
.headerimg {
border: 1px solid #f2f;
box-shadow: 0px 0px 11px 5px #f0f;
}
.body {
margin: 0 auto;
max-width: 800px;
background-color: #21082b;
padding: 8px;
box-shadow: 0px 0px 11px 5px #f0f;
}
.form {
margin: auto;
text-align: center;
}
input[type="text"], input[type="submit"] {
border: 1px #f9f groove;
margin: 4px auto;
}
input[type="text"] {
width: 99%;
max-width: 700px;
border-color: #d6a9d6;
}
input {
background-color: #683c7a;
font-size: 24px;
border-radius: 4px;
}
.body, input {
color: #d6a9d6;
border: 1px #ff3b3b groove;
}
select {
background-color: #683c7a;
color: #d6a9d6;
font-size: 18px;
border-radius: 4px;
max-width: 700px;
border: 1px #f9f groove;
padding: 8px;
width: 200px;
}
select, input.langchange {
height: 35px;
vertical-align: middle;
}
.submit, .footer, h1 {
text-align: center;
margin-top: 32px;
}
input.langchange {
font-size: 24px;
}

29
urloli.1 Normal file
View File

@@ -0,0 +1,29 @@
.TH URLロリ 1 urloli\-VERSION
.SH ソフト名
URLロリ - クッソ小さいURL短縮作成ソフトだわ〜♡
.SH 概要
.B urloli
[\fI\,オプション\/\fR] [\fI\,ポート番号\/\fR]
.SH 説明
.PP
URLロリはクッソ小さいURL短縮作成ソフトです。
.TP
.TP
\fB\-s [ポート番号]\fR
ポート番号でサーバーを開始デフォルト9910
.TP
.TP
.B オプションなし
ローカルにURLを短縮
.SH 会話
.PP
XMPP: xmpp:moriyajinja@chat.xmpp.076.ne.jp?join
.br
IRC: irc.076.moe/6697 #moriyajinja
.br
メーリングリスト: (開発中)
.SH バグ報告
.PP
バグは下記のURLまでご報告下さい
.br
https://gitler.moe/suwako/urloli/issues

4
view/404.html Normal file
View File

@@ -0,0 +1,4 @@
{{template "header" .}}
{{ .Err }}<br />
<a href="/">{{.T "totop"}}</a>
{{template "footer" .}}

14
view/footer.html Normal file
View File

@@ -0,0 +1,14 @@
{{define "footer"}}
</div>
<p class="footer">
<a href="https://technicalsuwako.moe/blog/urloli-{{.Ves}}.xhtml">
urloli-{{.Ver}}
</a> |
<a href="https://gitler.moe/suwako/urloli">
<img src="/static/git.png" alt="Git" />
</a> |
<a href="https://076.moe/"></a>
</p>
</body>
</html>
{{end}}

22
view/header.html Normal file
View File

@@ -0,0 +1,22 @@
{{define "header"}}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type" />
<meta name="title" content="URLロリ" />
<meta name="description" content="クッソ小さいURL短縮作成ソフトだわ〜♡" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>URLロリ〜{{.Tit}}</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico" />
<link rel="stylesheet" type="text/css" href="/static/style.css" />
</head>
<body>
<h1>
<img class="headerimg" src="/static/logo.jpg" alt="{{.T "logo"}}" />
</h1>
<div class="body">
<p>
<a href="/">{{.T "top"}}</a>
</p>
{{end}}

26
view/index.html Normal file
View File

@@ -0,0 +1,26 @@
{{template "header" .}}
<div class="form">
<form action="/" method="POST">
<select class="langchange" name="lang">
<option value="ja"{{if eq .Lan "ja"}} selected{{end}}>日本語</option>
<option value="en"{{if eq .Lan "en"}} selected{{end}}>English</option>
</select>
<input
class="langchange"
type="submit"
name="langchange"
value="{{.T "langchange"}}"
/>
</form>
</div>
<hr />
{{.T "enterurl"}}
<div class="form">
<form action="/" method="POST">
<input type="text" name="newadd" value="" />
<div class="submit">
<input type="submit" name="sosin" value="{{.T "submit"}}" />
</div>
</form>
</div>
{{template "footer" .}}

7
view/submitted.html Normal file
View File

@@ -0,0 +1,7 @@
{{template "header" .}}
{{.T "canunderaccess"}}
<br />
<a href="{{ .Dom }}/{{ .Url }}">{{ .Dom }}/{{ .Url }}</a>
<br /><br />
<a href="/">{{.T "totop"}}</a>
{{template "footer" .}}