Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:使用 sqlite 记录未分块文件的文件名 #37

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:

- name: Build Linux arm64
run: |
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o tgState main.go
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -o tgState main.go

- name: Zip Linux amd64
run: |
Expand Down
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ mk.txt
.idea/*
.vscode/*
tmp/*
.air.toml
.vercel
.air.toml
.vercel
/.env
/files.db
/tgState.zip
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ https://tgstate.vercel.app / https://tgstate.ikun123.com/
不限制大小demo(临时) http://tgstate-cdn.ikun123.com/

演示图片:
<details>
<summary>NSFW</summary>

![tgState](https://tgstate.vercel.app/d/BQACAgUAAx0EcyK3ugACByxlOR-Nfl4esavoO4zdaYIP_k1KYQACDAsAAkf4yFVpf_awaEkS8jAE)
</details>


# 参数说明

Expand Down
4 changes: 4 additions & 0 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ Hosted on Vercel, resource limitations - files larger than 5MB are not supported

Demo image:

<details>
<summary>NSFW</summary>

![tgState](https://tgstate.vercel.app/d/BQACAgUAAx0EcyK3ugACByxlOR-Nfl4esavoO4zdaYIP_k1KYQACDAsAAkf4yFVpf_awaEkS8jAE)
</details>

# Parameter Description

Expand Down
4 changes: 2 additions & 2 deletions api/vercel.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ func Vercel(w http.ResponseWriter, r *http.Request) {
}
switch path {
case "/api":
// 调用 control 包中的 UploadImageAPI 处理函数
control.Middleware(control.UploadImageAPI)(w, r)
// 调用 control 包中的 UploadAPI 处理函数
control.Middleware(control.UploadAPI)(w, r)
case "/pwd":
control.Pwd(w, r)
default:
Expand Down
2 changes: 1 addition & 1 deletion assets/templates/files.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{template "public/header" .}}
<h1>上传文件到 Telegram</h1><label for="uploadFile" id="uploadFileLabel" class="custom-file-label">选择文件</label> <input
type="file" name="image" id="uploadFile" class="custom-file-input" multiple> <button id="uploadButton">上传</button>
type="file" name="file" id="uploadFile" class="custom-file-input" multiple> <button id="uploadButton">上传</button>
<div id="loading">上传中...</div>
<div id="response" class="ui-widget"></div>
{{template "public/footer" .}}
28 changes: 23 additions & 5 deletions assets/templates/footer.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
function uploadImg(e, ms) {
return new Promise((resolve, reject) => {
var o = new FormData();
o.append("image", e);
o.append("file", e);
var isImage = e.type.startsWith('image/');
$("#uploadButton").prop("disabled", !0);
$("#uploadButton").text("上传中");
Expand All @@ -69,12 +69,14 @@
(a = a + ":" + window.location.port),
$.ajax({
type: "POST",
url: a + "/api",
url: window.location.href.replace(/\/$/, "") + "/api",
data: o,
contentType: !1,
processData: !1,
success: function (e) {
var link = a + e.message;

var link = window.location.href + e.message;
var proxyUrl = e.proxyUrl
var t;
if (e.code == 1) {
if (ms) {
Expand All @@ -90,6 +92,17 @@
link +
')">Markdown</span><span class="copy-code" data-clipboard-text="[img]' +
link +
'[/img]">BBCode</span></div></div>'+
'<div class="response-item response-success">上传成功,代理图片外链:<a target="_blank" href="' +
proxyUrl +
'">' +
proxyUrl +
'</a><div class="copy-links"><span class="copy-code" data-clipboard-text="&lt;img src=&quot;' +
proxyUrl +
'&quot; alt=&quot;Your Alt Text&quot;&gt;">HTML</span><span class="copy-code" data-clipboard-text="![Alt Text](' +
proxyUrl +
')">Markdown</span><span class="copy-code" data-clipboard-text="[img]' +
link +
'[/img]">BBCode</span></div></div>'
);
} else {
Expand All @@ -98,8 +111,13 @@
link +
'">' +
link +
"</a></div>"
);
"</a></div>" +
'<div class="response-item response-success">上传成功,代理文件外链:<a target="_blank" href="' +
proxyUrl +
'">' +
proxyUrl +
"</a></div>"
)
}
}
resolve(e.message);
Expand Down
11 changes: 11 additions & 0 deletions build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@echo off
REM 设置目标操作系统为 Linux,架构为 64 位
set GOOS=freebsd
set GOARCH=amd64

REM 编译 Go 程序并输出为指定的二进制文件名
go build -o tgState main.go

REM 提示编译完成
echo 编译完成,生成了 Linux 版本的二进制文件 tgState
pause
3 changes: 3 additions & 0 deletions build_freebsd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#/bin/bash
# fix Failed to create table:Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub
sed -i '' 's/go 1.20/go 1.17/' go.mod && go mod tidy && CGO_ENABLED=1 GOOS=freebsd GOARCH=amd64 go build -o tgState main.go
16 changes: 13 additions & 3 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@ package conf
var BotToken string
var ChannelName string
var Pass string
var ApiPass string
var Mode string
var BaseUrl string
var AllowedExts string
var ProxyUrl string

type UploadResponse struct {
Code int `json:"code"`
Message string `json:"message"`
ImgUrl string `json:"url"`
Code int `json:"code"`
Message string `json:"message"`
ImgUrl string `json:"url"`
ProxyUrl string `json:"proxyUrl"`
}

type ResponseResult struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}

const FileRoute = "/d/"
111 changes: 86 additions & 25 deletions control/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package control

import (
"encoding/json"
"fmt"
"html/template"
"io"
"log"
"net/http"
"net/url"
"path/filepath"
"strconv"
"strings"
Expand All @@ -16,12 +18,14 @@ import (
"csz.net/tgstate/utils"
)

// UploadImageAPI 上传图片api
func UploadImageAPI(w http.ResponseWriter, r *http.Request) {
// UploadAPI 上传图片api
func UploadAPI(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")

if r.Method == http.MethodPost {
// 获取上传的文件
file, header, err := r.FormFile("image")
file, header, err := r.FormFile("file")

if err != nil {
errJsonMsg("Unable to get file", w)
// http.Error(w, "Unable to get file", http.StatusBadRequest)
Expand All @@ -35,7 +39,14 @@ func UploadImageAPI(w http.ResponseWriter, r *http.Request) {
}
// 检查文件类型
allowedExts := []string{".jpg", ".jpeg", ".png"}
ext := filepath.Ext(header.Filename)

// 如果设置了AllowedExts,则使用设置的文件类型
if len(conf.AllowedExts) > 0 {
allowedExts = append(allowedExts, strings.Split(conf.AllowedExts, ",")...)
}

var fileName = header.Filename
ext := filepath.Ext(fileName)
valid := false
for _, allowedExt := range allowedExts {
if ext == allowedExt {
Expand All @@ -44,20 +55,34 @@ func UploadImageAPI(w http.ResponseWriter, r *http.Request) {
}
}
if conf.Mode != "p" && !valid {
errJsonMsg("Invalid file type. Only .jpg, .jpeg, and .png are allowed.", w)
errJsonMsg(fmt.Sprintf("Invalid file type. Only .jpg, .jpeg, and .png %s are allowed.", conf.AllowedExts), w)
// http.Error(w, "Invalid file type. Only .jpg, .jpeg, and .png are allowed.", http.StatusBadRequest)
return
}
res := conf.UploadResponse{
Code: 0,
Message: "error",
}
img := conf.FileRoute + utils.UpDocument(utils.TgFileData(header.Filename, file))
if img != conf.FileRoute {
fileId := utils.UpDocument(utils.TgFileData(fileName, file))
if "blob" != fileName {
ip := r.RemoteAddr // 获取上传者IP
// 插入数据到数据库
err := SaveFileRecord(fileId, fileName, ip)
if err != nil {
errJsonMsg("Unable to save file record", w)
}
}

downloadUrl := conf.FileRoute + fileId
if downloadUrl != conf.FileRoute {
imageUrl := strings.TrimSuffix(conf.BaseUrl, "/") + downloadUrl
// url encode imageUrl
proxyUrl := conf.ProxyUrl + "/" + url.QueryEscape(imageUrl)
res = conf.UploadResponse{
Code: 1,
Message: img,
ImgUrl: strings.TrimSuffix(conf.BaseUrl, "/") + img,
Code: 1,
Message: downloadUrl,
ImgUrl: imageUrl,
ProxyUrl: proxyUrl,
}
}
w.Header().Set("Content-Type", "application/json")
Expand All @@ -69,29 +94,24 @@ func UploadImageAPI(w http.ResponseWriter, r *http.Request) {
// 如果不是POST请求,返回错误响应
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
}
func errJsonMsg(msg string, w http.ResponseWriter) {
// 这里示例直接返回JSON响应
response := conf.UploadResponse{
Code: 0,
Message: msg,
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}

// D 下载文件
func D(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
id := strings.TrimPrefix(path, conf.FileRoute)
if id == "" {
fileId := strings.TrimPrefix(path, conf.FileRoute)
if fileId == "" {
// 设置响应的状态码为 404
w.WriteHeader(http.StatusNotFound)
// 写入响应内容
w.Write([]byte("404 Not Found"))
errJsonMsg("404 Not Found", w)
return
}

record, err := GetFileNameByIDOrName(fileId)
if err == nil && record.FileId != "" {
fileId = record.FileId
}
// 发起HTTP GET请求来获取Telegram图片
fileUrl, _ := utils.GetDownloadUrl(id)
fileUrl, _ := utils.GetDownloadUrl(fileId)
resp, err := http.Get(fileUrl)
if err != nil {
http.Error(w, "Failed to fetch content", http.StatusInternalServerError)
Expand Down Expand Up @@ -156,6 +176,11 @@ func D(w http.ResponseWriter, r *http.Request) {
} else {
// 使用DetectContentType函数检测文件类型
w.Header().Set("Content-Type", http.DetectContentType(buffer))

if err == nil && record.Filename != "" {
w.Header().Set("Content-Disposition", "attachment; filename=\""+record.Filename+"\"")
}

_, err = w.Write(buffer[:n])
if err != nil {
http.Error(w, "Failed to write content", http.StatusInternalServerError)
Expand Down Expand Up @@ -270,6 +295,42 @@ func Pwd(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusSeeOther)
}

func FilesAPI(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
password := r.URL.Query().Get("password")
response := conf.ResponseResult{
Code: 0,
Message: "ok",
}

if conf.ApiPass != "" && password != conf.ApiPass {
response.Message = "Unauthorized"
response.Code = http.StatusUnauthorized
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(response)
return
}

record, err := SelectAllRecord()
response.Data = record
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}

func errJsonMsg(msg string, w http.ResponseWriter) {
// 这里示例直接返回JSON响应
response := conf.UploadResponse{
Code: 0,
Message: msg,
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}

func Middleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 只有当密码设置并且不为"none"时,才进行检查
Expand Down
Loading