Compare commits

...

2 commits

Author SHA1 Message Date
諏訪子 2292704b7e . 2023-06-29 12:01:33 +09:00
テクニカル諏訪子 1a318e581f コメント付き 2023-06-27 13:48:35 +09:00
7 changed files with 79 additions and 48 deletions

2
.gitignore vendored
View file

@ -1 +1,3 @@
/hozonsite /hozonsite
/bin
/*.tar.gz

View file

@ -8,39 +8,42 @@ import (
) )
type Config struct { type Config struct {
configpath string configpath, webpath, datapath, domain string
webpath string
datapath string
domain string
} }
func getconf () Config { func getconf () Config {
var payload map[string]interface{} var payload map[string]interface{}
var cnf Config var cnf Config
// バイナリ、データ、及びFreeBSDとNetBSDの場合、コンフィグ
prefix := "/usr" prefix := "/usr"
// BSDだけはただの/usrではない
if runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" { if runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" {
prefix += "/local" prefix += "/local"
} else if runtime.GOOS == "netbsd" { } else if runtime.GOOS == "netbsd" {
prefix += "/pkg" prefix += "/pkg"
} }
// コンフィグファイル
cnf.configpath = "/etc/hozonsite/config.json" cnf.configpath = "/etc/hozonsite/config.json"
//_, err = os.Stat(cnf.configpath)
cnf.datapath = prefix + "/share/hozonsite" cnf.datapath = prefix + "/share/hozonsite"
// また、FreeBSDとNetBSDだけは違う場所だ。OpenBSDは正しい場所
// FreeBSD = /usr/local/etc/hozonsite/config.json
// NetBSD = /usr/pkg/etc/hozonsite/config.json
if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" { if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" {
cnf.configpath = prefix + cnf.configpath cnf.configpath = prefix + cnf.configpath
} }
// コンフィグファイルがなければ、死ね
data, err := os.ReadFile(cnf.configpath) data, err := os.ReadFile(cnf.configpath)
if err != nil { if err != nil {
fmt.Println("エラー:", err) fmt.Println("エラー:", err)
} }
json.Unmarshal(data, &payload) json.Unmarshal(data, &payload)
cnf.webpath = payload["webpath"].(string) cnf.webpath = payload["webpath"].(string) // データパス
cnf.domain = payload["domain"].(string) cnf.domain = payload["domain"].(string) // ドメイン名
payload = nil payload = nil // もういらなくなった
return cnf return cnf
} }

View file

@ -8,6 +8,7 @@ import (
"strings" "strings"
) )
// URLでパラメートル?、=等)がある場合
func stripurl (url string) string { func stripurl (url string) string {
res := strings.ReplaceAll(url, "?", "") res := strings.ReplaceAll(url, "?", "")
res = strings.ReplaceAll(res, "=", "") res = strings.ReplaceAll(res, "=", "")
@ -15,26 +16,30 @@ func stripurl (url string) string {
} }
func getpage (url string, path string) { func getpage (url string, path string) {
// ページを読み込む
curl, err := http.Get(url) curl, err := http.Get(url)
if err != nil { if err != nil {
fmt.Println("CURLエラー", err) fmt.Println("CURLエラー", err)
return return
} }
defer curl.Body.Close() // ソフトの終了する時に実行する
defer curl.Body.Close() // ページの内容を読み込む
body, err2 := io.ReadAll(curl.Body) body, err2 := io.ReadAll(curl.Body)
if err2 != nil { if err2 != nil {
fmt.Println("読込エラー:", err2) fmt.Println("読込エラー:", err2)
return return
} }
// 空index.htmlファイルを創作する
fn, err3 := os.Create(path + "/index.html") fn, err3 := os.Create(path + "/index.html")
if err3 != nil { if err3 != nil {
fmt.Println("ファイルの創作エラー:", err3) fmt.Println("ファイルの創作エラー:", err3)
return return
} }
defer fn.Close() // ソフトの終了する時に実行する
defer fn.Close() // あのindex.htmlファイルに内容をそのまま書き込む
_, err4 := fn.WriteString(string(body)) _, err4 := fn.WriteString(string(body))
if err4 != nil { if err4 != nil {
fmt.Println("ファイル書込エラー:", err4) fmt.Println("ファイル書込エラー:", err4)

42
main.go
View file

@ -18,50 +18,50 @@ func help () {
} }
func main () { func main () {
cnf := getconf() cnf := getconf() // コンフィグファイル
args := os.Args args := os.Args // コマンドラインのパラメートル
if len(args) == 2 { if len(args) == 2 {
if args[1] == "-v" { if args[1] == "-v" { // バージョンを表示
fmt.Println("hozonsite-" + version) fmt.Println("hozonsite-" + version)
return return
} else if args[1] == "-s" { } else if args[1] == "-s" { // :9920でウェブサーバーを実行
serv(cnf, 9920) serv(cnf, 9920)
} else if args[1] == "-h" { } else if args[1] == "-h" { // ヘルプを表示
help() help()
return return
} else { } else { // コマンドラインでウェブサイトを保存
if checkprefix(args[1]) { if checkprefix(args[1]) { // プロトコールはあってるかどうか
eurl := stripurl(args[1]) eurl := stripurl(args[1]) // パラメートルの文字(?、=等)を削除
exist := checkexist(eurl, cnf.datapath) exist := checkexist(eurl, cnf.datapath) // 既に/usr/share/hozonsite/archiveに存在するかどうか
var confirm string var confirm string // 既に存在したら、使う
if len(exist) > 0 { if len(exist) > 0 { // あ、既に存在する
fmt.Println("このページが既に保存されているみたいです。") fmt.Println("このページが既に保存されているみたいです。")
fmt.Println("本当に手続きましょうか? [y/N]") fmt.Println("本当に手続きましょうか? [y/N]")
for _, ex := range exist { for _, ex := range exist { // 既に存在するページのURLを表示
fmt.Println(strings.Replace(ex, cnf.datapath, cnf.domain, 1)) fmt.Println(strings.Replace(ex, cnf.datapath, cnf.domain, 1))
} }
fmt.Scanf("%s", &confirm) fmt.Scanf("%s", &confirm)
} }
if len(exist) == 0 || confirm == "y" || confirm == "Y" { if len(exist) == 0 || confirm == "y" || confirm == "Y" { // 存在しない OR 「本当に手続きましょうか」でYを入力した場合
path := mkdirs(eurl, cnf.datapath) path := mkdirs(eurl, cnf.datapath)
getpage(args[1], path) getpage(args[1], path) // ページをダウンロード
scanpage(path, eurl, cnf.datapath) scanpage(path, eurl, cnf.datapath) // 色々の必須な編集
fmt.Println(cnf.domain + strings.Replace(path, cnf.datapath, "", 1)) fmt.Println(cnf.domain + strings.Replace(path, cnf.datapath, "", 1)) // 新しいURLを表示
} }
return return
} else { } else { // 結局HTTPかHTTPSじゃないわね…
fmt.Println("URLは不正です。終了…") fmt.Println("URLは不正です。終了…")
return return
} }
} }
} else if len(args) == 3 && args[1] == "-s" { } else if len(args) == 3 && args[1] == "-s" { // 好みなポート番号でウェブサーバーを実行
if port, err := strconv.Atoi(args[2]); err != nil { if port, err := strconv.Atoi(args[2]); err != nil { // でも、数字じゃないかもしん
fmt.Printf("%qは数字ではありません。\n", args[2]) fmt.Printf("%qは数字ではありません。\n", args[2])
return return
} else { } else { // OK、実行しよ〜
serv(cnf, port) serv(cnf, port)
} }
} else { } else { // パラメートルは不明の場合、ヘルプを表示
help() help()
return return
} }

View file

@ -8,10 +8,12 @@ import (
"path/filepath" "path/filepath"
) )
// HTTPかHTTPSの確認
func checkprefix (url string) bool { func checkprefix (url string) bool {
return strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") return strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://")
} }
// ページは既に存在するの?
func checkexist (url string, prefix string) []string { func checkexist (url string, prefix string) []string {
res, err := filepath.Glob(prefix + "/archive/*" + url2path(url)) res, err := filepath.Glob(prefix + "/archive/*" + url2path(url))
if err != nil { if err != nil {
@ -20,6 +22,7 @@ func checkexist (url string, prefix string) []string {
return res return res
} }
// http:/かhttps:/はいらない。最後の「/」は必要
func url2path (url string) string { func url2path (url string) string {
res := "" res := ""
if strings.HasPrefix(url, "https:/") { if strings.HasPrefix(url, "https:/") {
@ -35,6 +38,7 @@ func url2path (url string) string {
return res return res
} }
// 必要なフォルダの創作
func mkdirs (url string, prefix string) string { func mkdirs (url string, prefix string) string {
rep := url2path(url) rep := url2path(url)
t := time.Now().Unix() t := time.Now().Unix()

View file

@ -13,25 +13,29 @@ import (
) )
func scanpage (path string, domain string, thisdomain string) error { func scanpage (path string, domain string, thisdomain string) error {
// 先に保存したページを読み込む
fn, err := os.ReadFile(path + "/index.html") fn, err := os.ReadFile(path + "/index.html")
if err != nil { if err != nil {
return err return err
} }
/* 削除 */ // 要らないタグを削除
var script = regexp.MustCompile(`(<script.*</script>)`).ReplaceAllString(string(fn), "") var script = regexp.MustCompile(`(<script.*</script>)`).ReplaceAllString(string(fn), "")
var noscript = regexp.MustCompile(`(<noscript.*</noscript>)`).ReplaceAllString(string(script), "") var noscript = regexp.MustCompile(`(<noscript.*</noscript>)`).ReplaceAllString(string(script), "")
var audio = regexp.MustCompile(`(<audio.*</audio>)`).ReplaceAllString(string(noscript), "") var audio = regexp.MustCompile(`(<audio.*</audio>)`).ReplaceAllString(string(noscript), "")
var video = regexp.MustCompile(`(<video.*</video>)`).ReplaceAllString(string(audio), "") var video = regexp.MustCompile(`(<video.*</video>)`).ReplaceAllString(string(audio), "")
var iframe = regexp.MustCompile(`(<iframe.*</iframe>)`).ReplaceAllString(string(video), "") var iframe = regexp.MustCompile(`(<iframe.*</iframe>)`).ReplaceAllString(string(video), "")
/* 追加ダウンロード+ローカル化 */ // 追加ダウンロード+ローカル化
var ass = regexp.MustCompile(`(<img.*src=['"]|<meta.*content=['"]|<link.*href=['"])(.*\.)(png|webp|jpg|jpeg|gif|css|js|ico|svg|ttf|woff2)(\?[^'"]*)?`) var ass = regexp.MustCompile(`(<img.*src=['"]|<meta.*content=['"]|<link.*href=['"])(.*\.)(png|webp|jpg|jpeg|gif|css|js|ico|svg|ttf|woff2)(\?[^'"]*)?`)
// 必要であれば、ページ内のURLを修正
spath := "static/" spath := "static/"
if !strings.HasSuffix(path, "/") { if !strings.HasSuffix(path, "/") {
spath = "/" + spath spath = "/" + spath
} }
spath = path + spath spath = path + spath
// また、追加ダウンロードのファイルに上記のフォルダを創作
err = os.Mkdir(spath, 0755) err = os.Mkdir(spath, 0755)
if err != nil { if err != nil {
return err return err
@ -40,52 +44,57 @@ func scanpage (path string, domain string, thisdomain string) error {
repmap := make(map[string]string) repmap := make(map[string]string)
for _, cssx := range ass.FindAllString(iframe, -1) { for _, cssx := range ass.FindAllString(iframe, -1) {
// ページ内のURLを受け取る
s := regexp.MustCompile(`(.*src=['"]|.*content=['"]|.*href=['"])`).Split(cssx, -1) s := regexp.MustCompile(`(.*src=['"]|.*content=['"]|.*href=['"])`).Split(cssx, -1)
ss := regexp.MustCompile(`(['"].*)`).Split(s[1], -1) ss := regexp.MustCompile(`(['"].*)`).Split(s[1], -1)
ogurl := ss[0] ogurl := ss[0] // 変わる前に元のURLを保存して
// URLは//で始まるは愛
if strings.HasPrefix(ss[0], "//") { if strings.HasPrefix(ss[0], "//") {
ss[0] = "https:" + ss[0] ss[0] = "https:" + ss[0]
} }
// ファイル名を見つけて
fss := strings.Split(ss[0], "/") fss := strings.Split(ss[0], "/")
assdom := "" assdom := ""
filename := fss[len(fss)-1] filename := fss[len(fss)-1]
// httpかhttpsで始まる場合
if strings.HasPrefix(ss[0], "http://") || strings.HasPrefix(ss[0], "https://") { if strings.HasPrefix(ss[0], "http://") || strings.HasPrefix(ss[0], "https://") {
assdom = fss[2] assdom = fss[2]
} }
// フォルダの創作
asspath := path + "/static/" + assdom asspath := path + "/static/" + assdom
err = os.MkdirAll(asspath, 0755) err = os.MkdirAll(asspath, 0755)
if err != nil { if err != nil { // 出来なければ、死ね
return err return err
} }
if filename == "" { if filename == "" { // ファイル名がなければ、次に値にスキップしてね
continue continue
} }
if strings.HasPrefix(ss[0], "http://") || strings.HasPrefix(ss[0], "https://") { if strings.HasPrefix(ss[0], "http://") || strings.HasPrefix(ss[0], "https://") { // httpかhttpsで始まったら、ダウンロードだけしよう
err = dlres(ss[0], filepath.Join(asspath, filename)) err = dlres(ss[0], filepath.Join(asspath, filename))
if err != nil { if err != nil { // 出来なければ、死ね
return err return err
} }
} else { } else { // ローカルファイルなら、ちょっと変更は必要となるかしら
u, err := url.Parse(domain) u, err := url.Parse(domain)
if err != nil { if err != nil { // 出来なければ、死ね
return err return err
} }
rel, err := url.Parse(ss[0]) rel, err := url.Parse(ss[0])
if err != nil { if err != nil { // 出来なければ、死ね
return err return err
} }
af := u.ResolveReference(rel).String() af := u.ResolveReference(rel).String()
err = dlres(af, filepath.Join(asspath, filename)) err = dlres(af, filepath.Join(asspath, filename))
if err != nil { if err != nil { // 出来なければ、死ね
return err return err
} }
} }
@ -95,26 +104,29 @@ func scanpage (path string, domain string, thisdomain string) error {
repmap[ogurl] = filepath.Join("/static", filename) repmap[ogurl] = filepath.Join("/static", filename)
} }
if err != nil { if err != nil { // 出来なければ、死ね
fmt.Println(err) fmt.Println(err)
return errors.New("2. ダウンロードに失敗:") return errors.New("ダウンロードに失敗:")
} }
} }
// URLをローカル化
for ourl, lurl := range repmap { for ourl, lurl := range repmap {
aurl := strings.ReplaceAll(path, thisdomain, "") + stripver(lurl) aurl := strings.ReplaceAll(path, thisdomain, "") + stripver(lurl)
iframe = strings.ReplaceAll(iframe, ourl, aurl) iframe = strings.ReplaceAll(iframe, ourl, aurl)
} }
// index.htmlファイルを更新する
err = os.WriteFile(path + "/index.html", []byte(iframe), 0644) err = os.WriteFile(path + "/index.html", []byte(iframe), 0644)
if err != nil { if err != nil { // 出来なければ、死ね
fmt.Println(err) fmt.Println(err)
return errors.New("書込に失敗") return errors.New("書込に失敗")
} }
return nil return nil // エラーが出なかったから、返すのは不要
} }
// 画像、JS、CSS等ファイルのURLでパラメートルがある場合
func stripver (durl string) string { func stripver (durl string) string {
u, err := url.Parse(durl) u, err := url.Parse(durl)
if err != nil { if err != nil {
@ -134,7 +146,7 @@ func dlres (durl string, dest string) error {
} }
defer res.Body.Close() defer res.Body.Close()
dest = stripver(dest) dest = stripver(dest) // URLでパラメートルがあれば、消す
// MIMEタイプを確認 // MIMEタイプを確認
ct := res.Header.Get("Content-Type") ct := res.Header.Get("Content-Type")

7
srv.go
View file

@ -16,7 +16,7 @@ type (
Tit, Err, Lan, Ver, Ves, Url, Body string Tit, Err, Lan, Ver, Ves, Url, Body string
Ext []Exist // 既に存在する場合 Ext []Exist // 既に存在する場合
} }
Stat struct { Stat struct { // APIのみ
Url, Ver string Url, Ver string
} }
Exist struct { Exist struct {
@ -24,6 +24,7 @@ type (
} }
) )
// 日本語か英語 TODO複数言語対応
func initloc (r *http.Request) string { func initloc (r *http.Request) string {
cookie, err := r.Cookie("lang") cookie, err := r.Cookie("lang")
if err == nil && cookie.Value == "en" { if err == nil && cookie.Value == "en" {
@ -44,6 +45,7 @@ func tspath (p string) string {
return "" return ""
} }
// ホームページ
func siteHandler (cnf Config) func (http.ResponseWriter, *http.Request) { func siteHandler (cnf Config) func (http.ResponseWriter, *http.Request) {
return func (w http.ResponseWriter, r *http.Request) { return func (w http.ResponseWriter, r *http.Request) {
ftmpl := []string{cnf.webpath + "/view/index.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"} ftmpl := []string{cnf.webpath + "/view/index.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"}
@ -124,6 +126,7 @@ func siteHandler (cnf Config) func (http.ResponseWriter, *http.Request) {
} }
} }
// /api TODO
func apiHandler (cnf Config) func (http.ResponseWriter, *http.Request) { func apiHandler (cnf Config) func (http.ResponseWriter, *http.Request) {
return func (w http.ResponseWriter, r *http.Request) { return func (w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
@ -133,6 +136,7 @@ func apiHandler (cnf Config) func (http.ResponseWriter, *http.Request) {
} }
} }
// /archive
func archiveHandler (cnf Config) func (http.ResponseWriter, *http.Request) { func archiveHandler (cnf Config) func (http.ResponseWriter, *http.Request) {
return func (w http.ResponseWriter, r *http.Request) { return func (w http.ResponseWriter, r *http.Request) {
ftmpl := []string{cnf.webpath + "/view/index.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"} ftmpl := []string{cnf.webpath + "/view/index.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"}
@ -180,6 +184,7 @@ func archiveHandler (cnf Config) func (http.ResponseWriter, *http.Request) {
} }
} }
// サーバー
func serv (cnf Config, port int) { func serv (cnf Config, port int) {
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))