Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05978f7fc3 | ||
|
|
4c3a508b84 | ||
|
|
6733ccd4b8 | ||
|
|
2f0c09ff72 | ||
|
|
ff3ed3a57e | ||
|
|
39ca01c3e8 | ||
|
|
c457cb1daf | ||
|
|
73efeb0acf | ||
|
|
afcc33b76b | ||
|
|
6319c18ddd | ||
|
|
cb73169eeb | ||
|
|
b47327dfe6 | ||
|
|
097ca9b8c0 |
14
Dockerfile
14
Dockerfile
@@ -1,16 +1,16 @@
|
|||||||
FROM frolvlad/alpine-glibc:alpine-3.9_glibc-2.29
|
|
||||||
RUN apk update \
|
|
||||||
&& apk add --no-cache \
|
|
||||||
ca-certificates \
|
|
||||||
bash jq curl \
|
|
||||||
php php-sqlite3 php-pdo php-pdo_mysql php-json php-pdo_sqlite
|
|
||||||
|
|
||||||
|
FROM golang:1.13-alpine as certs
|
||||||
|
RUN apk update && apk add --no-cache ca-certificates
|
||||||
|
|
||||||
|
FROM busybox:glibc
|
||||||
RUN mkdir -p /var/log
|
RUN mkdir -p /var/log
|
||||||
WORKDIR /main
|
WORKDIR /main
|
||||||
|
COPY --from=certs /etc/ssl/certs /etc/ssl/certs
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ENV GOPATH=""
|
ENV GOPATH=""
|
||||||
ENV MNT="/mnt/"
|
ENV MNT="/mnt/"
|
||||||
ENTRYPOINT ["/bin/bash", "/main/entrypoint.sh"]
|
ENTRYPOINT ["/main/exec-todo-server"]
|
||||||
CMD []
|
CMD []
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ var (
|
|||||||
StoreAddr string
|
StoreAddr string
|
||||||
StoreUser string
|
StoreUser string
|
||||||
StorePass string
|
StorePass string
|
||||||
|
Root string
|
||||||
MyTinyTodo string
|
MyTinyTodo string
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ func Refresh() {
|
|||||||
as.Append(args.STRING, "storeuser", "user of store", "")
|
as.Append(args.STRING, "storeuser", "user of store", "")
|
||||||
as.Append(args.STRING, "storepass", "pass of store", "")
|
as.Append(args.STRING, "storepass", "pass of store", "")
|
||||||
as.Append(args.STRING, "mtt", "url of php server", "http://localhost:38808")
|
as.Append(args.STRING, "mtt", "url of php server", "http://localhost:38808")
|
||||||
|
as.Append(args.STRING, "root", "root of static files", "./public")
|
||||||
if err := as.Parse(); err != nil {
|
if err := as.Parse(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -41,5 +43,6 @@ func Refresh() {
|
|||||||
StoreAddr = as.Get("storeaddr").GetString()
|
StoreAddr = as.Get("storeaddr").GetString()
|
||||||
StoreUser = as.Get("storeuser").GetString()
|
StoreUser = as.Get("storeuser").GetString()
|
||||||
StorePass = as.Get("storepass").GetString()
|
StorePass = as.Get("storepass").GetString()
|
||||||
|
Root = as.Get("root").GetString()
|
||||||
MyTinyTodo = as.Get("mtt").GetString()
|
MyTinyTodo = as.Get("mtt").GetString()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
#! /bin/bash
|
|
||||||
|
|
||||||
(
|
|
||||||
pushd /main/testdata/mytinytodo*
|
|
||||||
php -S 0.0.0.0:38808
|
|
||||||
kill -9 1
|
|
||||||
) &
|
|
||||||
|
|
||||||
(
|
|
||||||
until curl localhost:39909; do
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
pushd /main/testdata
|
|
||||||
bash ./migrate.sh 192.168.0.86:44112 localhost:39909
|
|
||||||
) &
|
|
||||||
|
|
||||||
exec /main/exec-todo-server "$@"
|
|
||||||
@@ -61,6 +61,18 @@ func ToStrArr(k string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ToTime(s string) time.Time {
|
func ToTime(s string) time.Time {
|
||||||
v, _ := time.Parse("2006-01-02 15:04:05", s)
|
v, err := time.Parse("2006-01-02 15:04:05", s)
|
||||||
|
if err != nil || v.IsZero() {
|
||||||
|
v, err = time.Parse("2006-01-02", s)
|
||||||
|
}
|
||||||
|
if err != nil || v.IsZero() {
|
||||||
|
v, err = time.Parse("1/2/06", s)
|
||||||
|
}
|
||||||
|
if err != nil || v.IsZero() {
|
||||||
|
v, err = time.Parse("02 Jan 2006 3:04 PM", s)
|
||||||
|
}
|
||||||
|
if err != nil || v.IsZero() {
|
||||||
|
v, err = time.Parse("02 Jan 2006 3:04 PM", strings.ReplaceAll(s, "+", " "))
|
||||||
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,18 @@ func TestToTime(t *testing.T) {
|
|||||||
in: "5",
|
in: "5",
|
||||||
out: "0001-01-01 00:00:00",
|
out: "0001-01-01 00:00:00",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
in: "1/2/03",
|
||||||
|
out: "2003-01-02 00:00:00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: "11/12/03",
|
||||||
|
out: "2003-11-12 00:00:00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: "01+Jan+2020+12:00+PM",
|
||||||
|
out: "2020-01-01 12:00:00",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ func (a *Ajax) storageListTasks(listID string, filters ...func(t *task.Task) boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if filtered {
|
if filtered {
|
||||||
|
//task.Title = fmt.Sprintf("[%d] %s", task.Index, task.Title)
|
||||||
tasks = append(tasks, task)
|
tasks = append(tasks, task)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,41 +7,21 @@ import (
|
|||||||
"local/todo-server/server/ajax/form"
|
"local/todo-server/server/ajax/form"
|
||||||
"local/todo-server/server/ajax/task"
|
"local/todo-server/server/ajax/task"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type taskWithDelta struct {
|
||||||
|
task *task.Task
|
||||||
|
delta int
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Ajax) loadTasks(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) loadTasks(w http.ResponseWriter, r *http.Request) error {
|
||||||
listID, _, _ := a.Cur(r)
|
listID, _, _ := a.Cur(r)
|
||||||
filterComplete := func(t *task.Task) bool {
|
filterComplete := filterComplete(form.Get(r, "compl"))
|
||||||
if form.Get(r, "compl") == "" {
|
filterTags := filterTags(form.ToStrArr(form.Get(r, "t")))
|
||||||
return true
|
filterSubstr := filterSubstr(form.Get(r, "s"))
|
||||||
}
|
|
||||||
return form.Get(r, "compl") == "1" || !t.Complete
|
|
||||||
}
|
|
||||||
filterTags := func(t *task.Task) bool {
|
|
||||||
if form.Get(r, "t") == "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
whitelistTags := form.ToStrArr(form.Get(r, "t"))
|
|
||||||
if len(whitelistTags) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
whitelistTagMap := make(map[string]struct{})
|
|
||||||
for _, tag := range whitelistTags {
|
|
||||||
whitelistTagMap[tag] = struct{}{}
|
|
||||||
}
|
|
||||||
for _, tag := range t.Tags {
|
|
||||||
if _, ok := whitelistTagMap[tag]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
filterSubstr := func(t *task.Task) bool {
|
|
||||||
substr := form.Get(r, "s")
|
|
||||||
return substr == "" || strings.Contains(fmt.Sprintf("%+v", t), substr)
|
|
||||||
}
|
|
||||||
tasks, err := a.storageListTasks(listID, filterComplete, filterTags, filterSubstr)
|
tasks, err := a.storageListTasks(listID, filterComplete, filterTags, filterSubstr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -49,6 +29,36 @@ func (a *Ajax) loadTasks(w http.ResponseWriter, r *http.Request) error {
|
|||||||
return json.NewEncoder(w).Encode(map[string]interface{}{"list": tasks})
|
return json.NewEncoder(w).Encode(map[string]interface{}{"list": tasks})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterComplete(compl string) func(t *task.Task) bool {
|
||||||
|
return func(t *task.Task) bool {
|
||||||
|
return compl == "" || !t.Complete || (compl == "1" && t.Complete)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterTags(tags []string) func(t *task.Task) bool {
|
||||||
|
return func(t *task.Task) bool {
|
||||||
|
for _, whitelisted := range tags {
|
||||||
|
found := false
|
||||||
|
for _, tag := range t.Tags {
|
||||||
|
if whitelisted == tag {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterSubstr(substr string) func(t *task.Task) bool {
|
||||||
|
return func(t *task.Task) bool {
|
||||||
|
found := substr == "" || strings.Contains(strings.ToLower(fmt.Sprintf("%+v %+v", t.Title, t.Tags)), strings.ToLower(substr))
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Ajax) newTask(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) newTask(w http.ResponseWriter, r *http.Request) error {
|
||||||
listID, newTask, err := a.makeTask(r)
|
listID, newTask, err := a.makeTask(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -83,7 +93,10 @@ func (a *Ajax) makeTask(r *http.Request) (string, *task.Task, error) {
|
|||||||
|
|
||||||
func (a *Ajax) deleteTask(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) deleteTask(w http.ResponseWriter, r *http.Request) error {
|
||||||
_, taskID, _ := a.Cur(r)
|
_, taskID, _ := a.Cur(r)
|
||||||
return a.storageDelTask(taskID)
|
if err := a.storageDelTask(taskID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.NewEncoder(w).Encode(map[string]interface{}{"total": 0})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Ajax) completeTask(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) completeTask(w http.ResponseWriter, r *http.Request) error {
|
||||||
@@ -107,45 +120,57 @@ func (a *Ajax) completeTask(w http.ResponseWriter, r *http.Request) error {
|
|||||||
|
|
||||||
func (a *Ajax) editNote(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) editNote(w http.ResponseWriter, r *http.Request) error {
|
||||||
listID, taskID, _ := a.Cur(r)
|
listID, taskID, _ := a.Cur(r)
|
||||||
task, err := a.storageGetTask(taskID)
|
editedTask, err := a.storageGetTask(taskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
task.SetNote(form.Get(r, "note"))
|
editedTask.SetNote(form.Get(r, "note"))
|
||||||
return a.storageSetTask(listID, task)
|
if err := a.storageSetTask(listID, editedTask); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.NewEncoder(w).Encode(map[string]interface{}{"total": 1, "list": []*task.Task{editedTask}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Ajax) editTask(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) editTask(w http.ResponseWriter, r *http.Request) error {
|
||||||
listID, task, err := a.makeTask(r)
|
listID, editedTask, err := a.makeTask(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, taskID, _ := a.Cur(r)
|
_, taskID, _ := a.Cur(r)
|
||||||
task.UUID = taskID
|
editedTask.UUID = taskID
|
||||||
return a.storageSetTask(listID, task)
|
if err := a.storageSetTask(listID, editedTask); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.NewEncoder(w).Encode(map[string]interface{}{"total": 1, "list": []*task.Task{editedTask}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Ajax) setPrio(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) setPrio(w http.ResponseWriter, r *http.Request) error {
|
||||||
listID, taskID, _ := a.Cur(r)
|
listID, taskID, _ := a.Cur(r)
|
||||||
task, err := a.storageGetTask(taskID)
|
editedTask, err := a.storageGetTask(taskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
task.SetPrio(form.ToInt(form.Get(r, "prio")))
|
editedTask.SetPrio(form.ToInt(form.Get(r, "prio")))
|
||||||
return a.storageSetTask(listID, task)
|
if err := a.storageSetTask(listID, editedTask); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.NewEncoder(w).Encode(map[string]interface{}{"total": 1, "list": []*task.Task{editedTask}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Ajax) moveTask(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) moveTask(w http.ResponseWriter, r *http.Request) error {
|
||||||
_, taskID, _ := a.Cur(r)
|
_, taskID, _ := a.Cur(r)
|
||||||
toList := form.Get(r, "to")
|
toList := form.Get(r, "to")
|
||||||
task, err := a.storageGetTask(taskID)
|
movedTask, err := a.storageGetTask(taskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := a.storageDelTask(taskID); err != nil {
|
if err := a.storageDelTask(taskID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return a.storageSetTask(toList, task)
|
if err := a.storageSetTask(toList, movedTask); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.NewEncoder(w).Encode(map[string]interface{}{"total": 1, "list": []*task.Task{movedTask}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Ajax) parseTaskStr(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) parseTaskStr(w http.ResponseWriter, r *http.Request) error {
|
||||||
@@ -155,8 +180,8 @@ func (a *Ajax) parseTaskStr(w http.ResponseWriter, r *http.Request) error {
|
|||||||
func (a *Ajax) changeOrder(w http.ResponseWriter, r *http.Request) error {
|
func (a *Ajax) changeOrder(w http.ResponseWriter, r *http.Request) error {
|
||||||
order := form.Get(r, "order")
|
order := form.Get(r, "order")
|
||||||
orders := strings.Split(order, "&")
|
orders := strings.Split(order, "&")
|
||||||
sum := 0
|
modified := make([]taskWithDelta, 0)
|
||||||
zero := ""
|
indices := make([]int, 0)
|
||||||
for _, order := range orders {
|
for _, order := range orders {
|
||||||
taskIDDelta := strings.Split(order, "=")
|
taskIDDelta := strings.Split(order, "=")
|
||||||
if len(taskIDDelta) < 2 {
|
if len(taskIDDelta) < 2 {
|
||||||
@@ -164,43 +189,34 @@ func (a *Ajax) changeOrder(w http.ResponseWriter, r *http.Request) error {
|
|||||||
}
|
}
|
||||||
taskID := taskIDDelta[0]
|
taskID := taskIDDelta[0]
|
||||||
delta, _ := strconv.Atoi(taskIDDelta[1])
|
delta, _ := strconv.Atoi(taskIDDelta[1])
|
||||||
if delta < 0 {
|
|
||||||
delta = -1
|
|
||||||
} else {
|
|
||||||
delta = 0
|
|
||||||
}
|
|
||||||
task, err := a.storageGetTask(taskID)
|
task, err := a.storageGetTask(taskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
listID, err := a.taskIDToListID(taskID)
|
modified = append(modified, taskWithDelta{task: task, delta: delta})
|
||||||
|
indices = append(indices, task.Index)
|
||||||
|
}
|
||||||
|
sort.Slice(modified, func(i, j int) bool {
|
||||||
|
if modified[i].delta < modified[j].delta {
|
||||||
|
return true
|
||||||
|
} else if modified[i].delta > modified[j].delta {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return modified[i].task.Index < modified[j].task.Index
|
||||||
|
})
|
||||||
|
sort.Ints(indices)
|
||||||
|
for i := 0; i < len(modified); i++ {
|
||||||
|
task := *modified[i].task
|
||||||
|
task.Index = indices[i]
|
||||||
|
listID, err := a.taskIDToListID(task.UUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
task.Index += delta
|
if err := a.storageSetTask(listID, &task); err != nil {
|
||||||
if err := a.storageSetTask(listID, task); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if delta == 0 {
|
|
||||||
zero = taskID
|
|
||||||
}
|
}
|
||||||
sum += delta
|
|
||||||
}
|
fmt.Fprintf(w, `{"total":1}`)
|
||||||
if zero == "" || sum == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
task, err := a.storageGetTask(zero)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
listID, err := a.taskIDToListID(zero)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
task.Index -= sum
|
|
||||||
if err := a.storageSetTask(listID, task); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprint(w, `{"total":1}`)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func New(r *http.Request) (*Task, error) {
|
|||||||
UUID: form.NewUUID(),
|
UUID: form.NewUUID(),
|
||||||
Title: form.Get(r, "title"),
|
Title: form.Get(r, "title"),
|
||||||
Priority: form.ToInt(form.Get(r, "prio")),
|
Priority: form.ToInt(form.Get(r, "prio")),
|
||||||
Tags: StrList(form.ToStrArr(form.Get(r, "tags"))),
|
Tags: append(StrList(form.ToStrArr(form.Get(r, "tag"))), StrList(form.ToStrArr(form.Get(r, "tags")))...),
|
||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
Edited: time.Now(),
|
Edited: time.Now(),
|
||||||
|
|
||||||
@@ -110,6 +110,13 @@ func (t *Task) MarshalJSON() ([]byte, error) {
|
|||||||
"dueInt": t.Due.Unix(),
|
"dueInt": t.Due.Unix(),
|
||||||
"dueTitle": "Due ",
|
"dueTitle": "Due ",
|
||||||
}
|
}
|
||||||
|
if t.Due.IsZero() {
|
||||||
|
for k := range m {
|
||||||
|
if strings.HasPrefix(k, "due") {
|
||||||
|
delete(m, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if t.Complete {
|
if t.Complete {
|
||||||
m["dateCompleted"] = t.Completed.Format(fullFormat)
|
m["dateCompleted"] = t.Completed.Format(fullFormat)
|
||||||
m["dateCompletedInline"] = t.Completed.Format(shortFormat)
|
m["dateCompletedInline"] = t.Completed.Format(shortFormat)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ajax
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"local/todo-server/server/ajax/form"
|
||||||
"local/todo-server/server/ajax/task"
|
"local/todo-server/server/ajax/task"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@@ -207,3 +208,49 @@ func TestAjaxChangeOrder(t *testing.T) {
|
|||||||
t.Error(tasks[2])
|
t.Error(tasks[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFilterComplete(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
query string
|
||||||
|
task bool
|
||||||
|
out bool
|
||||||
|
}{
|
||||||
|
{query: "", out: true, task: false},
|
||||||
|
{query: "0", out: true, task: false},
|
||||||
|
{query: "1", out: true, task: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, c := range cases {
|
||||||
|
task := &task.Task{Complete: c.task}
|
||||||
|
out := filterComplete(c.query)
|
||||||
|
if out(task) != c.out {
|
||||||
|
t.Errorf("[%d] want %v, got %v", name, c.out, out(task))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterTags(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
query string
|
||||||
|
tags []string
|
||||||
|
out bool
|
||||||
|
}{
|
||||||
|
"no filter": {query: "", out: true},
|
||||||
|
"single matching filter": {query: "a", out: true, tags: []string{"a"}},
|
||||||
|
"single non-matching filter": {query: "a", out: false, tags: []string{"b"}},
|
||||||
|
"duo matching filter": {query: "a, b", out: true, tags: []string{"b", "a"}},
|
||||||
|
"duo partial-matching filter": {query: "a, c", out: false, tags: []string{"b", "a"}},
|
||||||
|
"duo non-matching filter": {query: "a, c", out: false, tags: []string{"d", "e"}},
|
||||||
|
"trio matching filter": {query: "a, b, c", out: true, tags: []string{"b", "a", "c", "d", "e"}},
|
||||||
|
"trio partial-matching filter": {query: "a, c, d", out: false, tags: []string{"b", "a", "d"}},
|
||||||
|
"trio non-matching filter": {query: "a, b, c", out: false, tags: []string{"x", "y", "z"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, c := range cases {
|
||||||
|
task := &task.Task{Tags: c.tags}
|
||||||
|
out := filterTags(form.ToStrArr(c.query))
|
||||||
|
if v := out(task); v != c.out {
|
||||||
|
t.Errorf("[%s] want %v, got %v", name, c.out, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ package server
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"local/gziphttp"
|
||||||
"local/router"
|
"local/router"
|
||||||
"local/todo-server/config"
|
"local/todo-server/config"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) Routes() error {
|
func (s *Server) Routes() error {
|
||||||
@@ -19,12 +20,20 @@ func (s *Server) Routes() error {
|
|||||||
handler http.HandlerFunc
|
handler http.HandlerFunc
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
path: fmt.Sprintf("%s%s", router.Wildcard, router.Wildcard),
|
path: "/",
|
||||||
handler: s.phpProxy,
|
handler: s.gzip(s.index),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: fmt.Sprintf("ajax.php"),
|
path: "/mytinytodo_lang.php",
|
||||||
handler: s.HandleAjax,
|
handler: s.gzip(s.lang),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: fmt.Sprintf("%s%s", router.Wildcard, router.Wildcard),
|
||||||
|
handler: s.gzip(s.phpProxy),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/ajax.php",
|
||||||
|
handler: s.gzip(s.HandleAjax),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,10 +45,39 @@ func (s *Server) Routes() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) lang(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, `
|
||||||
|
mytinytodo.lang.init({
|
||||||
|
confirmDelete: "Are you sure you want to delete the task?",
|
||||||
|
confirmLeave: "There can be unsaved data. Do you really want to leave?",
|
||||||
|
actionNoteSave: "save",
|
||||||
|
actionNoteCancel: "cancel",
|
||||||
|
error: "Some error occurred (click for details)",
|
||||||
|
denied: "Access denied",
|
||||||
|
invalidpass: "Wrong password",
|
||||||
|
tagfilter: "Tag:",
|
||||||
|
addList: "Create new list",
|
||||||
|
addListDefault: "Todo",
|
||||||
|
renameList: "Rename list",
|
||||||
|
deleteList: "This will delete current list with all tasks in it.\nAre you sure?",
|
||||||
|
clearCompleted: "This will delete all completed tasks in the list.\nAre you sure?",
|
||||||
|
settingsSaved: "Settings saved. Reloading...",
|
||||||
|
daysMin: ["Su","Mo","Tu","We","Th","Fr","Sa"],
|
||||||
|
daysLong: ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
|
||||||
|
monthsLong: ["January","February","March","April","May","June","July","August","September","October","November","December"],
|
||||||
|
tags: "Tags",
|
||||||
|
tasks: "Tasks",
|
||||||
|
f_past: "Overdue",
|
||||||
|
f_today: "Today and tomorrow",
|
||||||
|
f_soon: "Soon"
|
||||||
|
});
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) index(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) index(w http.ResponseWriter, r *http.Request) {
|
||||||
f, err := os.Open(path.Join(config.MyTinyTodo, "index.php"))
|
f, err := os.Open(path.Join(config.Root, "index.html"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
@@ -47,11 +85,37 @@ func (s *Server) index(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) phpProxy(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) phpProxy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch filepath.Ext(r.URL.Path) {
|
||||||
|
case ".php":
|
||||||
|
default:
|
||||||
|
s.static(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
url, err := url.Parse(config.MyTinyTodo)
|
url, err := url.Parse(config.MyTinyTodo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
} else {
|
} else {
|
||||||
proxy := httputil.NewSingleHostReverseProxy(url)
|
log.Println("WOULD proxy", url.String(), r.URL.Path)
|
||||||
proxy.ServeHTTP(w, r)
|
s.index(w, r)
|
||||||
|
//proxy := httputil.NewSingleHostReverseProxy(url)
|
||||||
|
//proxy.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) static(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.fileServer.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) gzip(h http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if gziphttp.Can(r) {
|
||||||
|
gz := gziphttp.New(w)
|
||||||
|
defer gz.Close()
|
||||||
|
w = gz
|
||||||
|
}
|
||||||
|
if filepath.Ext(r.URL.Path) == ".css" {
|
||||||
|
w.Header().Set("Content-Type", "text/css; charset=utf-8")
|
||||||
|
}
|
||||||
|
h(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"local/router"
|
"local/router"
|
||||||
|
"local/todo-server/config"
|
||||||
"local/todo-server/server/ajax"
|
"local/todo-server/server/ajax"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
*ajax.Ajax
|
*ajax.Ajax
|
||||||
*router.Router
|
*router.Router
|
||||||
|
fileServer http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Server {
|
func New() *Server {
|
||||||
@@ -15,8 +18,10 @@ func New() *Server {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
fileServer := http.FileServer(http.Dir(config.Root))
|
||||||
return &Server{
|
return &Server{
|
||||||
Ajax: ajax,
|
Ajax: ajax,
|
||||||
Router: router.New(),
|
Router: router.New(),
|
||||||
|
fileServer: fileServer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
source_to_run_loaded.sh
Normal file → Executable file
0
source_to_run_loaded.sh
Normal file → Executable file
2
testdata/migrate.sh
vendored
Normal file → Executable file
2
testdata/migrate.sh
vendored
Normal file → Executable file
@@ -114,6 +114,6 @@ function b64decode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if [ "$0" == "$BASH_SOURCE" ]; then
|
if [ "$0" == "$BASH_SOURCE" ]; then
|
||||||
echo bash migrate.sh 192.168.0.86:44112 localhost:39909
|
echo bash migrate.sh 192.168.0.86:44112 localhost:38809
|
||||||
time main "$@"
|
time main "$@"
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user