バージョン2.0.0に
このコミットが含まれているのは:
コミット
3f7614956a
|
@ -0,0 +1,15 @@
|
|||
# 2.0.0
|
||||
* Makefile化
|
||||
* ソースコードは複数ファイルに分ける
|
||||
* コマンドラインからURL短熟
|
||||
* バージョンの表示
|
||||
* ヘルプの表示
|
||||
* manページ
|
||||
* ポート番号はご自由に決める様にした(デフォルトは9910)
|
||||
|
||||
# 1.0.0
|
||||
* PHPからGoに交換しました
|
||||
* 今度からバージョンを付きます
|
||||
|
||||
# それ以前
|
||||
* 色々
|
|
@ -0,0 +1,45 @@
|
|||
NAME=urloli
|
||||
VERSION=2.0.0
|
||||
# Linux、Cruxの場合は必須。他のディストリビューションはどうでも良い
|
||||
PREFIX=/usr
|
||||
# FreeBSDとOpenBSD
|
||||
#PREFIX=/usr/local
|
||||
MANPREFIX=${PREFIX}/share/man
|
||||
# LinuxとOpenBSD
|
||||
CNFPREFIX=/etc
|
||||
# FreeBSD
|
||||
#CNFPREFIX=/usr/local/etc
|
||||
CC=CGO_ENABLED=0 go build
|
||||
# リリース。なし=デバッグ。
|
||||
RELEASE=-ldflags="-s -w" -buildvcs=false
|
||||
|
||||
all:
|
||||
${CC} ${RELEASE} -o ${NAME} $<
|
||||
|
||||
clean:
|
||||
rm -f ${NAME} ${NAME}-${VERSION}.tar.gz
|
||||
|
||||
dist: clean
|
||||
mkdir -p ${NAME}${VERSION}
|
||||
cp -R LICENSE.txt Makefile README.md CHANGELOG.md\
|
||||
view static logo.jpg\
|
||||
${NAME}.1 *.go *.json ${NAME}-${VERSION}
|
||||
tar -zcfv ${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}/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 options clean dist install uninstall
|
267
README.md
267
README.md
|
@ -2,15 +2,272 @@
|
|||
クッソ小さいURL短縮作成ソフトだわ〜♡
|
||||
|
||||
## インストールする方法
|
||||
|
||||
### 従属ソフト
|
||||
|
||||
* Go 1.19以上
|
||||
* nginx又はOpenBSDのhttpd
|
||||
* 良いOS (GNU/Linux、OpenBSD、又はFreeBSD)
|
||||
|
||||
## インストールする方法
|
||||
|
||||
### 全部(opendoasを使わなければ、sudoをご利用、又はopendoasをインストールして下さい)
|
||||
|
||||
```sh
|
||||
chmod +x install.sh && doas ./install.sh domain.tld
|
||||
make
|
||||
doas make install
|
||||
```
|
||||
|
||||
## links.jsonファイルの中に
|
||||
### OpenBSD
|
||||
|
||||
```sh
|
||||
nvim /etc/rc.d/urloli
|
||||
```
|
||||
{
|
||||
"hogehoge": "https://076.moe"
|
||||
|
||||
```
|
||||
#!/bin/ksh
|
||||
#
|
||||
# $OpenBSD: urloli.rc,v 1.4 2018/01/11 19:27:11 rpe Exp $
|
||||
|
||||
name="urloli"
|
||||
daemon="/usr/local/bin/${name}"
|
||||
|
||||
. /etc/rc.d/rc.subr
|
||||
|
||||
rc_cmd $1
|
||||
```
|
||||
|
||||
```sh
|
||||
nvim /etc/rc.conf.local
|
||||
```
|
||||
|
||||
```
|
||||
relayd_flags=
|
||||
pkg_scripts=urloli
|
||||
```
|
||||
|
||||
```sh
|
||||
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
|
||||
```
|
||||
|
||||
```
|
||||
table <urloli> { IPADDRESS }
|
||||
|
||||
http protocol "httpproxy" {
|
||||
pass request quick header "Host" value "DOMAIN" forward to <urloli>
|
||||
block
|
||||
}
|
||||
|
||||
relay "proxy" {
|
||||
listen on * port 443 tls
|
||||
protocol "httpproxy"
|
||||
|
||||
forward to * port 9910
|
||||
}
|
||||
```
|
||||
|
||||
https://(ドメイン名)/hogehoge にアクセスすると、https://076.moe に移転されます。
|
||||
### その他
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"os"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
configpath string
|
||||
linkpath string
|
||||
webpath string
|
||||
domain 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"
|
||||
}
|
||||
|
||||
cnf.configpath = "/etc/urloli/config.json"
|
||||
cnf.linkpath = "/etc/urloli/links.json"
|
||||
if runtime.GOOS == "freebsd" {
|
||||
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 _, 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)
|
||||
payload = nil
|
||||
|
||||
return cnf, nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"domain": "https://urlo.li",
|
||||
"webpath": "/www/active/urlo.li"
|
||||
"webpath": "/var/www/htdocs/urloli"
|
||||
}
|
||||
|
|
80
install.sh
80
install.sh
|
@ -1,80 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
unamestr=$(uname)
|
||||
domain="$1"
|
||||
|
||||
case "$domain" in
|
||||
*.i2p) network="i2p" ;;
|
||||
*.onion) network="tor" ;;
|
||||
*) network="www" ;;
|
||||
esac
|
||||
|
||||
if [ "$unamestr" = 'FreeBSD' ]; then
|
||||
mkdir /usr/local/etc/urloli
|
||||
cp -i links.sample.json /usr/local/etc/urloli/links.json
|
||||
cp -i config.json /usr/local/etc/urloli/config.json
|
||||
sed -i .orig "s/urlo\.li/$domain/g" /usr/local/etc/urloli/config.json
|
||||
rm -rf /usr/local/etc/urloli/config.json.orig
|
||||
go build -buildvcs=false
|
||||
else
|
||||
mkdir /etc/urloli
|
||||
cp -i links.sample.json /etc/urloli/links.json
|
||||
cp -i config.json /etc/urloli/config.json
|
||||
sed -i "s/urlo\.li/$domain/g" /etc/urloli/config.json
|
||||
go build
|
||||
fi
|
||||
|
||||
mv -i urloli /usr/local/bin
|
||||
|
||||
if [ "$unamestr" = 'Linux' ]; then
|
||||
platform=$(cat /etc/os-release | grep "^ID=")
|
||||
if [ "$platform" = 'ID=debian' -o "$platform" = "ID=devuan" -o "$platform" = "ID=ubuntu" ]; then
|
||||
apt update && apt install certbot
|
||||
elif [ "$platform" = "ID=arch" -o "$platform" = "ID=artix" ]; then
|
||||
pacman -S certbot
|
||||
elif [ "$platform" = "ID=centos" -o "$platform" = "ID=rhel" ]; then
|
||||
dnf install certbot
|
||||
fi
|
||||
if [ "$network" = 'www' ]; then
|
||||
certbot certonly --webroot -d $domain -d www.$domain
|
||||
cp -i srv/linux/etc/nginx/sites-enabled/urloli-clear.conf /etc/nginx/sites-enabled/urloli.conf
|
||||
else
|
||||
cp -i srv/linux/etc/nginx/sites-enabled/urloli-dark.conf /etc/nginx/sites-enabled/urloli.conf
|
||||
fi
|
||||
sed -i "s/urlo\.li/$domain/g" /etc/nginx/sites-enabled/urloli.conf
|
||||
cp -i srv/linux/etc/init.d/urloli /etc/init.d
|
||||
chmod +x /etc/init.d/urloli
|
||||
/etc/init.d/urloli start
|
||||
elif [ "$unamestr" = 'OpenBSD' ]; then
|
||||
if [ "$network" = 'www' ]; then
|
||||
cat /etc/acme-client.conf srv/openbsd/etc/acme-client.conf > /etc/acme-client.conf
|
||||
sed -i "s/urlo\.li/$domain/g" /etc/acme-client.conf
|
||||
cat /etc/httpd.conf srv/openbsd/etc/httpd-clear.conf > /etc/httpd.conf
|
||||
else
|
||||
cat /etc/httpd.conf srv/openbsd/etc/httpd-dark.conf > /etc/httpd.conf
|
||||
fi
|
||||
sed -i "s/urlo\.li/$domain/g" /etc/httpd.conf
|
||||
rcctl restart httpd
|
||||
if [ "$network" = 'www' ]; then
|
||||
acme-client -v $domain
|
||||
fi
|
||||
cp -i srv/openbsd/etc/rc.d/urloli /etc/rc.d
|
||||
chmod +x /etc/rc.d/urloli
|
||||
rcctl start urloli
|
||||
elif [ "$unamestr" = 'FreeBSD' ]; then
|
||||
pkg install py39-certbot
|
||||
if [ "$network" = 'www' ]; then
|
||||
certbot certonly --webroot -d $domain -d www.$domain
|
||||
cp -i srv/linux/etc/nginx/sites-enabled/urloli-clear.conf /usr/local/etc/nginx/sites-enabled/urloli.conf
|
||||
else
|
||||
cp -i srv/linux/etc/nginx/sites-enabled/urloli-dark.conf /usr/local/etc/nginx/sites-enabled/urloli.conf
|
||||
fi
|
||||
sed -i .orig "s/urlo\.li/$domain/g" /usr/local/etc/nginx/sites-enabled/urloli.conf
|
||||
rm -rf /usr/local/etc/nginx/sites-enabled/urloli.conf.orig
|
||||
cp -i srv/freebsd/usr/local/etc/rc.d/urloli /usr/local/etc/rc.d
|
||||
chmod +x /usr/local/etc/rc.d/urloli
|
||||
sysrc urloli_enable=YES
|
||||
service start urloli
|
||||
fi
|
||||
|
||||
exit
|
269
main.go
269
main.go
|
@ -1,248 +1,61 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"text/template"
|
||||
"fmt"
|
||||
"strings"
|
||||
"net/http"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"unicode/utf8"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
linkpath string
|
||||
configpath string
|
||||
webpath string
|
||||
payload map[string]interface{}
|
||||
)
|
||||
var version = "2.0.0"
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// http://かhttps://で始まるかどうか
|
||||
func checkprefix (url string) bool {
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// URLは500文字以内かどうか
|
||||
func checkcharlim (url string) bool {
|
||||
if utf8.RuneCountInString(url) > 500 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func geturl (url string, checkjson bool) string {
|
||||
payload := getlinks()
|
||||
|
||||
for k := range payload {
|
||||
if checkjson {
|
||||
if url == payload[k] {
|
||||
return url
|
||||
}
|
||||
} else {
|
||||
if url == k {
|
||||
return payload[k].(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func insertjson (url string) string {
|
||||
payload := getlinks()
|
||||
|
||||
newstring := mkstring()
|
||||
payload[newstring] = url
|
||||
m, _ := json.Marshal(&payload)
|
||||
payload = nil
|
||||
ioutil.WriteFile(linkpath, m, os.ModePerm)
|
||||
// fmt.Printf("%s\n", m)
|
||||
|
||||
return newstring
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Tit string
|
||||
Err string
|
||||
Url string
|
||||
Dom string
|
||||
Lan string
|
||||
}
|
||||
|
||||
func getlinks () 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 getconfig () map[string]interface{} {
|
||||
data, err := ioutil.ReadFile(configpath)
|
||||
if err != nil {
|
||||
fmt.Println("config.jsonを開けられません: ", err)
|
||||
}
|
||||
|
||||
var payload map[string]interface{}
|
||||
json.Unmarshal(data, &payload)
|
||||
|
||||
return payload
|
||||
func help () {
|
||||
fmt.Println("使い方:");
|
||||
fmt.Println("urloli -v :バージョンを表示");
|
||||
fmt.Println("urloli -s [ポート番号] :ポート番号でウェブサーバーを実行(デフォルト=9910)");
|
||||
fmt.Println("urloli -h :ヘルプを表示");
|
||||
fmt.Println("urloli <URL> :コマンドラインでURLを短熟");
|
||||
}
|
||||
|
||||
func main () {
|
||||
if runtime.GOOS == "freebsd" {
|
||||
linkpath = "/usr/local/etc/urloli/links.json"
|
||||
configpath = "/usr/local/etc/urloli/config.json"
|
||||
} else {
|
||||
linkpath = "/etc/urloli/links.json"
|
||||
configpath = "/etc/urloli/config.json"
|
||||
cnf, err := getconf()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
args := os.Args
|
||||
|
||||
payload := getconfig()
|
||||
domain := payload["domain"].(string)
|
||||
webpath := payload["webpath"].(string)
|
||||
|
||||
payload = nil
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie, err := r.Cookie("lang")
|
||||
if err != nil {
|
||||
http.SetCookie(w, &http.Cookie {Name: "lang", Value: "ja", MaxAge: 31536000, Path: "/"})
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
if len(args) == 2 {
|
||||
if args[1] == "-v" {
|
||||
fmt.Println("urloli-" + version)
|
||||
return
|
||||
}
|
||||
|
||||
uri := r.URL.Path
|
||||
query := r.URL.Query()
|
||||
qnewurl := query.Get("newurl")
|
||||
data := &Page{Tit: "トップ", Lan: cookie.Value}
|
||||
if cookie.Value == "en" {
|
||||
data = &Page{Tit: "Top", Lan: cookie.Value}
|
||||
}
|
||||
tmpl := template.Must(template.ParseFiles(webpath + "/view/index.html", webpath + "/view/header.html", webpath + "/view/footer.html"))
|
||||
|
||||
if r.Method == "POST" {
|
||||
err := r.ParseForm()
|
||||
if err != nil { fmt.Println(err) }
|
||||
if r.PostForm.Get("sosin") != "" {
|
||||
if r.PostForm.Get("newadd") != "" {
|
||||
addurl := r.PostForm.Get("newadd")
|
||||
chkprx := checkprefix(addurl)
|
||||
chklim := checkcharlim(addurl)
|
||||
if !chkprx {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "不正なURL", Err: "URLは「http://」又は「https://」で始めます。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Invalid URL", Err: "The URL should start with \"http://\" or \"https://\".", Lan: cookie.Value}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(webpath + "/view/404.html", webpath + "/view/header.html", webpath + "/view/footer.html"))
|
||||
}
|
||||
if !chklim {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "不正なURL", Err: "URLは500文字以内です。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Invalid URL", Err: "The URL should be less than 500 characters.", Lan: cookie.Value}
|
||||
}
|
||||
data = &Page{Tit: "不正なURL", Err: ""}
|
||||
tmpl = template.Must(template.ParseFiles(webpath + "/view/404.html", webpath + "/view/header.html", webpath + "/view/footer.html"))
|
||||
}
|
||||
|
||||
if chklim && chkprx {
|
||||
chkfn := geturl(addurl, true)
|
||||
if chkfn != "" {
|
||||
http.Redirect(w, r, addurl, http.StatusSeeOther)
|
||||
return
|
||||
} else {
|
||||
res := insertjson(addurl)
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "短縮済み", Lan: cookie.Value, Url: res, Dom: domain}
|
||||
} else {
|
||||
data = &Page{Tit: "Shortened", Lan: cookie.Value, Url: res, Dom: domain}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(webpath + "/view/submitted.html", webpath + "/view/header.html", webpath + "/view/footer.html"))
|
||||
}
|
||||
}
|
||||
} else if args[1] == "-s" {
|
||||
serv(cnf, 9910)
|
||||
} else if args[1] == "-h" {
|
||||
help()
|
||||
return
|
||||
} else {
|
||||
if checkprefix(args[1]) {
|
||||
_, key := geturl(args[1], cnf.linkpath, true)
|
||||
if (key != "") {
|
||||
fmt.Println(cnf.domain + "/" + key)
|
||||
} else {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "未検出", Err: "URLをご入力下さい。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Not found", Err: "Please enter a URL.", Lan: cookie.Value}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(webpath + "/view/404.html", webpath + "/view/header.html", webpath + "/view/footer.html"))
|
||||
fmt.Println(cnf.domain + "/" + insertjson(args[1], cnf.linkpath))
|
||||
}
|
||||
} else if r.PostForm.Get("langchange") != "" {
|
||||
if cookie.Value == "ja" {
|
||||
http.SetCookie(w, &http.Cookie {Name: "lang", Value: "en"})
|
||||
} else {
|
||||
http.SetCookie(w, &http.Cookie {Name: "lang", Value: "ja"})
|
||||
}
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
} else {
|
||||
fmt.Println("URLは不正です。終了…")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if uri == "/" && qnewurl == "" {
|
||||
tmpl = template.Must(template.ParseFiles(webpath + "/view/index.html", webpath + "/view/header.html", webpath + "/view/footer.html"))
|
||||
} else if uri != "/" && qnewurl == "" {
|
||||
red := geturl(uri[1:], false)
|
||||
if red != "" {
|
||||
http.Redirect(w, r, red, http.StatusSeeOther)
|
||||
return
|
||||
} else {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "未検出", Err: "このURLを見つけられませんでした。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Not found", Err: "This URL could not be found.", Lan: cookie.Value}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(webpath + "/view/404.html", webpath + "/view/header.html", webpath + "/view/footer.html"))
|
||||
}
|
||||
} else if uri == "/" && qnewurl != "" {
|
||||
data = &Page{Tit: "短縮済み", Url: qnewurl, Dom: domain}
|
||||
tmpl = template.Must(template.ParseFiles(webpath + "/view/submitted.html", webpath + "/view/header.html", webpath + "/view/footer.html"))
|
||||
} else {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "未検出", Err: "このURLを見つけられませんでした。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Not found", Err: "This URL could not be found.", Lan: cookie.Value}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(webpath + "/view/404.html", webpath + "/view/header.html", webpath + "/view/footer.html"))
|
||||
}
|
||||
}
|
||||
|
||||
tmpl.Execute(w, data)
|
||||
data = nil
|
||||
})
|
||||
|
||||
fmt.Println("http://127.0.0.1:9910 でサーバーを実行中。終了するには、CTRL+Cを押して下さい。")
|
||||
http.ListenAndServe(":9910", nil)
|
||||
} else if len(args) == 3 && args[1] == "-s" {
|
||||
if port, err := strconv.Atoi(args[2]); err != nil {
|
||||
fmt.Printf("%qは数字ではありません。\n", args[2])
|
||||
return
|
||||
} else {
|
||||
serv(cnf, port)
|
||||
}
|
||||
} else {
|
||||
help()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"text/template"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Page struct {
|
||||
Tit string
|
||||
Err string
|
||||
Url string
|
||||
Dom string
|
||||
Lan string
|
||||
}
|
||||
|
||||
func serv (cnf Config, port int) {
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie, err := r.Cookie("lang")
|
||||
if err != nil {
|
||||
http.SetCookie(w, &http.Cookie {Name: "lang", Value: "ja", MaxAge: 31536000, Path: "/"})
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
uri := r.URL.Path
|
||||
query := r.URL.Query()
|
||||
qnewurl := query.Get("newurl")
|
||||
data := &Page{Tit: "トップ", Lan: cookie.Value}
|
||||
if cookie.Value == "en" {
|
||||
data = &Page{Tit: "Top", Lan: cookie.Value}
|
||||
}
|
||||
tmpl := template.Must(template.ParseFiles(cnf.webpath + "/view/index.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"))
|
||||
|
||||
if r.Method == "POST" {
|
||||
err := r.ParseForm()
|
||||
if err != nil { fmt.Println(err) }
|
||||
if r.PostForm.Get("sosin") != "" {
|
||||
if r.PostForm.Get("newadd") != "" {
|
||||
addurl := r.PostForm.Get("newadd")
|
||||
chkprx := checkprefix(addurl)
|
||||
chklim := checkcharlim(addurl)
|
||||
if !chkprx {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "不正なURL", Err: "URLは「http://」又は「https://」で始めます。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Invalid URL", Err: "The URL should start with \"http://\" or \"https://\".", Lan: cookie.Value}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(cnf.webpath + "/view/404.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"))
|
||||
}
|
||||
if !chklim {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "不正なURL", Err: "URLは500文字以内です。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Invalid URL", Err: "The URL should be less than 500 characters.", Lan: cookie.Value}
|
||||
}
|
||||
data = &Page{Tit: "不正なURL", Err: ""}
|
||||
tmpl = template.Must(template.ParseFiles(cnf.webpath + "/view/404.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"))
|
||||
}
|
||||
|
||||
if chklim && chkprx {
|
||||
chkfn, _ := geturl(addurl, cnf.linkpath, true)
|
||||
if chkfn != "" {
|
||||
http.Redirect(w, r, addurl, http.StatusSeeOther)
|
||||
return
|
||||
} else {
|
||||
res := insertjson(addurl, cnf.linkpath)
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "短縮済み", Lan: cookie.Value, Url: res, Dom: cnf.domain}
|
||||
} else {
|
||||
data = &Page{Tit: "Shortened", Lan: cookie.Value, Url: res, Dom: cnf.domain}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(cnf.webpath + "/view/submitted.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "未検出", Err: "URLをご入力下さい。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Not found", Err: "Please enter a URL.", Lan: cookie.Value}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(cnf.webpath + "/view/404.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"))
|
||||
}
|
||||
} else if r.PostForm.Get("langchange") != "" {
|
||||
if cookie.Value == "ja" {
|
||||
http.SetCookie(w, &http.Cookie {Name: "lang", Value: "en"})
|
||||
} else {
|
||||
http.SetCookie(w, &http.Cookie {Name: "lang", Value: "ja"})
|
||||
}
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if uri == "/" && qnewurl == "" {
|
||||
tmpl = template.Must(template.ParseFiles(cnf.webpath + "/view/index.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"))
|
||||
} else if uri != "/" && qnewurl == "" {
|
||||
red, _ := geturl(uri[1:], cnf.linkpath, false)
|
||||
if red != "" {
|
||||
http.Redirect(w, r, red, http.StatusSeeOther)
|
||||
return
|
||||
} else {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "未検出", Err: "このURLを見つけられませんでした。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Not found", Err: "This URL could not be found.", Lan: cookie.Value}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(cnf.webpath + "/view/404.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"))
|
||||
}
|
||||
} else if uri == "/" && qnewurl != "" {
|
||||
data = &Page{Tit: "短縮済み", Url: qnewurl, Dom: cnf.domain}
|
||||
tmpl = template.Must(template.ParseFiles(cnf.webpath + "/view/submitted.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"))
|
||||
} else {
|
||||
if cookie.Value == "ja" {
|
||||
data = &Page{Tit: "未検出", Err: "このURLを見つけられませんでした。", Lan: cookie.Value}
|
||||
} else {
|
||||
data = &Page{Tit: "Not found", Err: "This URL could not be found.", Lan: cookie.Value}
|
||||
}
|
||||
tmpl = template.Must(template.ParseFiles(cnf.webpath + "/view/404.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"))
|
||||
}
|
||||
}
|
||||
|
||||
tmpl.Execute(w, data)
|
||||
data = nil
|
||||
})
|
||||
|
||||
fmt.Println(fmt.Sprint("http://127.0.0.1:", port, " でサーバーを実行中。終了するには、CTRL+Cを押して下さい。"))
|
||||
http.ListenAndServe(fmt.Sprint(":", port), nil)
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
#!/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"
|
|
@ -1,42 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# chkconfig: 35 90 12
|
||||
# description: URL Loli server
|
||||
#
|
||||
|
||||
NAME=urloli
|
||||
DESC=urloli
|
||||
DAEMON=/usr/local/bin/$NAME
|
||||
|
||||
start () {
|
||||
echo "URLロリサーバーの開始:\n"
|
||||
sudo -u urloli /usr/local/bin/urloli &>/dev/null &
|
||||
echo
|
||||
}
|
||||
|
||||
stop () {
|
||||
echo "URLロリサーバーの終了: \n"
|
||||
pkill 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
|
||||
|
||||
exit 0
|
|
@ -1,40 +0,0 @@
|
|||
server {
|
||||
server_name urlo.li www.urlo.li;
|
||||
root /www/active/urlo.li;
|
||||
|
||||
access_log off;
|
||||
error_log off;
|
||||
|
||||
if ($host = www.urlo.li) {
|
||||
return 301 https://urlo.li$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/urlo.li/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/urlo.li/privkey.pem;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf
|
||||
}
|
||||
|
||||
server {
|
||||
if ($host = urlo.li) {
|
||||
return 301 https://urlo.li$request_uri;
|
||||
}
|
||||
|
||||
if ($host = www.urlo.li) {
|
||||
return 301 https://urlo.li$request_uri;
|
||||
}
|
||||
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name urlo.li www.urlo.li;
|
||||
return 404;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
server {
|
||||
server_name urlo.li;
|
||||
root /www/active/urlo.li;
|
||||
|
||||
access_log off;
|
||||
error_log off;
|
||||
|
||||
location /static {
|
||||
try_files $uri $uri/ /static/$args;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:9910;
|
||||
}
|
||||
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/sh -e
|
||||
exec 2>&1
|
||||
exec chpst urloli
|
|
@ -1,11 +0,0 @@
|
|||
[Unit]
|
||||
Description="URLロリ"
|
||||
Wants=network.target
|
||||
Before=network.target
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/urloli
|
||||
ExecStop=pkill urloli
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
domain urlo.li {
|
||||
alernative names { www.urlo.li }
|
||||
domain key "/etc/ssl/private/urlo.li.key"
|
||||
domain full chain certificate "/etc/ssl/urlo.li.fullchain.pem"
|
||||
sign with letsencrypt
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
|
||||
server "urlo.li" {
|
||||
listen on $ext_addr port 80
|
||||
location "/.well-known/acme-challenge/*" {
|
||||
root "/acme"
|
||||
request strip 2
|
||||
}
|
||||
block return 301 "https://$SERVER_NAME$REQUEST_URI"
|
||||
}
|
||||
server "urlo.li" {
|
||||
listen on $ext_addr tls port 443
|
||||
tls {
|
||||
certificate "/etc/letsencrypt/live/urlo.li/fullchain.pem"
|
||||
key "/etc/letsencrypt/live/urlo.li/privkey.pem"
|
||||
}
|
||||
connection { max requests 500, timeout 3600 }
|
||||
location "/*" {
|
||||
fastcgi socket tcp 127.0.0.1 9910
|
||||
}
|
||||
location "/.well-known/acme-challenge/*" {
|
||||
root "/acme"
|
||||
request strip 2
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
server "urlo.li" {
|
||||
listen on $ext_addr port 80
|
||||
connection { max requests 500, timeout 3600 }
|
||||
location "/*" {
|
||||
fastcgi socket tcp 127.0.0.1 9910
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/ksh
|
||||
#
|
||||
# $OpenBSD: urloli.rc,v 1.4 2018/01/11 19:27:11 rpe Exp $
|
||||
|
||||
name="urloli"
|
||||
daemon="/usr/local/bin/${name}"
|
||||
|
||||
. /etc/rc.d/rc.subr
|
||||
|
||||
rc_cmd $1
|
|
@ -0,0 +1,30 @@
|
|||
.TH URLロリ 1 urloli\-VERSION
|
||||
.SH ソフト名
|
||||
URLロリ - クッソ小さいURL短縮作成ソフトだわ〜♡
|
||||
.SH 概要
|
||||
.B urloli
|
||||
[\fI\,オプション\/\fR] [\fI\,ポート番号\/\fR]
|
||||
.SH 説明
|
||||
.PP
|
||||
URLロリはクッソ小さいURL短縮作成ソフトです。
|
||||
.TP
|
||||
\fB\-v\fR
|
||||
バージョンを表示
|
||||
.TP
|
||||
\fB\-s [ポート番号]\fR
|
||||
ポート番号でサーバーを開始(デフォルト=9910)
|
||||
.TP
|
||||
\fB\-h\fR
|
||||
ヘルプを表示
|
||||
.SH 会話
|
||||
.PP
|
||||
XMPP: xmpp:urloli@chat.xmpp.076.ne.jp?join
|
||||
.br
|
||||
IRC: irc.076.ne.jp/6697 #urloli
|
||||
.br
|
||||
メーリングリスト: (開発中)
|
||||
.SH バグ報告
|
||||
.PP
|
||||
バグは下記のURLまでご報告下さい:
|
||||
.br
|
||||
https://gitler.moe/suwako/urloli/issues
|
|
@ -1,7 +1,7 @@
|
|||
{{define "footer"}}
|
||||
</div>
|
||||
<p class="footer">
|
||||
<a href="https://gitler.moe/suwako/urloli.go"><img src="/static/git.png" alt="Git" /></a> |
|
||||
<a href="https://gitler.moe/suwako/urloli"><img src="/static/git.png" alt="Git" /></a> |
|
||||
<a href="https://076.moe/">076</a>
|
||||
</p>
|
||||
</body>
|
||||
|
|
読み込み中…
新しいイシューから参照