Commit 62a0d7a8a772705b98713e3be1dde7af2e928856
1 parent
0c71c54873
Exists in
master
minor changes
Showing
3 changed files
with
14 additions
and
17 deletions
Show diff stats
document/document.go
1 | package document | 1 | package document |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "errors" | 4 | "errors" |
5 | "fmt" | 5 | "fmt" |
6 | "io" | 6 | "io" |
7 | "mime" | 7 | "mime" |
8 | "net/http" | 8 | "net/http" |
9 | "os" | 9 | "os" |
10 | "time" | 10 | "time" |
11 | 11 | ||
12 | web "git.to-net.rs/marko.tikvic/webutility" | 12 | web "git.to-net.rs/marko.tikvic/webutility" |
13 | ) | 13 | ) |
14 | 14 | ||
15 | // Document ... | 15 | // Document ... |
16 | type Document struct { | 16 | type Document struct { |
17 | ID int64 `json:"id"` | 17 | ID int64 `json:"id"` |
18 | FileName string `json:"fileName"` | 18 | FileName string `json:"fileName"` |
19 | Extension string `json:"extension"` | 19 | Extension string `json:"extension"` |
20 | ContentType string `json:"contentType"` | 20 | ContentType string `json:"contentType"` |
21 | Size int64 `json:"fileSize"` | 21 | Size int64 `json:"fileSize"` |
22 | UploadedBy string `json:"uploadedBy"` | 22 | UploadedBy string `json:"uploadedBy"` |
23 | LastModifiedBy string `json:"lastModifiedBy"` | 23 | LastModifiedBy string `json:"lastModifiedBy"` |
24 | TimeUploaded int64 `json:"timeUploaded"` | 24 | TimeUploaded int64 `json:"timeUploaded"` |
25 | TimeLastModified int64 `json:"timeLastModified"` | 25 | TimeLastModified int64 `json:"timeLastModified"` |
26 | RoleAccessLevel int64 `json:"accessLevel"` | 26 | RoleAccessLevel int64 `json:"accessLevel"` |
27 | Description string `json:"description"` | 27 | Description string `json:"description"` |
28 | Download *DownloadLink `json:"download"` | 28 | Download *DownloadLink `json:"download"` |
29 | Path string `json:"-"` | 29 | Path string `json:"-"` |
30 | directory string | 30 | directory string |
31 | data []byte | 31 | data []byte |
32 | } | 32 | } |
33 | 33 | ||
34 | // OpenFileAsDocument ... | 34 | // OpenFileAsDocument ... |
35 | func OpenFileAsDocument(path string) (*Document, error) { | 35 | func OpenFileAsDocument(path string) (*Document, error) { |
36 | d := &Document{Path: path} | 36 | d := &Document{Path: path} |
37 | 37 | ||
38 | f, err := os.Open(d.Path) | 38 | f, err := os.Open(d.Path) |
39 | if err != nil { | 39 | if err != nil { |
40 | return nil, err | 40 | return nil, err |
41 | } | 41 | } |
42 | defer f.Close() | 42 | defer f.Close() |
43 | 43 | ||
44 | stats, err := f.Stat() | 44 | stats, err := f.Stat() |
45 | if err != nil { | 45 | if err != nil { |
46 | return nil, err | 46 | return nil, err |
47 | } | 47 | } |
48 | 48 | ||
49 | d.FileName = stats.Name() | 49 | d.FileName = stats.Name() |
50 | d.Size = stats.Size() | 50 | d.Size = stats.Size() |
51 | d.Extension = web.FileExtension(d.FileName) | 51 | d.Extension = web.FileExtension(d.FileName) |
52 | 52 | ||
53 | d.data = make([]byte, d.Size) | 53 | d.data = make([]byte, d.Size) |
54 | if _, err = f.Read(d.data); err != nil { | 54 | if _, err = f.Read(d.data); err != nil { |
55 | return nil, err | 55 | return nil, err |
56 | } | 56 | } |
57 | 57 | ||
58 | return d, err | 58 | return d, err |
59 | } | 59 | } |
60 | 60 | ||
61 | // DownloadLink ... | 61 | // DownloadLink ... |
62 | type DownloadLink struct { | 62 | type DownloadLink struct { |
63 | Method string `json:"method"` | 63 | Method string `json:"method"` |
64 | URL string `json:"url"` | 64 | URL string `json:"url"` |
65 | } | 65 | } |
66 | 66 | ||
67 | // SetDownloadInfo ... | 67 | // SetDownloadInfo ... |
68 | func (d *Document) SetDownloadInfo(method, url string) { | 68 | func (d *Document) SetDownloadInfo(method, url string) { |
69 | d.Download = &DownloadLink{ | 69 | d.Download = &DownloadLink{ |
70 | Method: method, | 70 | Method: method, |
71 | URL: url, | 71 | URL: url, |
72 | } | 72 | } |
73 | } | 73 | } |
74 | 74 | ||
75 | // ServeDocument writes d's buffer to w and sets appropriate headers according to d's content type | 75 | // ServeDocument writes d's buffer to w and sets appropriate headers according to d's content type |
76 | // and downloadPrompt. | 76 | // and downloadPrompt. |
77 | func ServeDocument(w http.ResponseWriter, d *Document, downloadPrompt bool) error { | 77 | func ServeDocument(w http.ResponseWriter, d *Document, downloadPrompt bool) error { |
78 | f, err := os.Open(d.Path) | 78 | f, err := os.Open(d.Path) |
79 | if err != nil { | 79 | if err != nil { |
80 | return err | 80 | return err |
81 | } | 81 | } |
82 | defer f.Close() | 82 | defer f.Close() |
83 | 83 | ||
84 | web.SetContentType(w, mime.TypeByExtension(d.Extension)) | 84 | web.SetContentType(w, mime.TypeByExtension(d.Extension)) |
85 | web.SetResponseStatus(w, http.StatusOK) | 85 | web.SetResponseStatus(w, http.StatusOK) |
86 | if downloadPrompt { | 86 | if downloadPrompt { |
87 | w.Header().Set("Content-Disposition", "attachment; filename="+d.FileName) | 87 | w.Header().Set("Content-Disposition", "attachment; filename="+d.FileName) |
88 | } | 88 | } |
89 | 89 | ||
90 | buf := make([]byte, d.Size) | 90 | buf := make([]byte, d.Size) |
91 | if _, err := f.Read(buf); err != nil { | 91 | if _, err := f.Read(buf); err != nil { |
92 | return err | 92 | return err |
93 | } | 93 | } |
94 | 94 | ||
95 | w.Header().Set("Content-Length", fmt.Sprintf("%d", d.Size)) | 95 | w.Header().Set("Content-Length", fmt.Sprintf("%d", d.Size)) |
96 | web.WriteResponse(w, buf) | 96 | web.WriteResponse(w, buf) |
97 | 97 | ||
98 | return nil | 98 | return nil |
99 | } | 99 | } |
100 | 100 | ||
101 | // ParseDocument ... | 101 | // ParseDocument ... |
102 | func ParseDocument(req *http.Request) (doc *Document, err error) { | 102 | func ParseDocument(req *http.Request) (doc *Document, err error) { |
103 | req.ParseMultipartForm(32 << 20) | 103 | req.ParseMultipartForm(32 << 20) |
104 | file, fheader, err := req.FormFile("document") | 104 | file, fheader, err := req.FormFile("document") |
105 | if err != nil { | 105 | if err != nil { |
106 | return doc, err | 106 | return doc, err |
107 | } | 107 | } |
108 | 108 | ||
109 | claims, _ := web.GetTokenClaims(req) | 109 | claims, _ := web.GetTokenClaims(req) |
110 | owner := claims.Username | 110 | owner := claims.Username |
111 | 111 | ||
112 | fname := fheader.Filename | 112 | fname := fheader.Filename |
113 | 113 | ||
114 | fsize := fheader.Size | 114 | fsize := fheader.Size |
115 | ftype := fmt.Sprintf("%v", fheader.Header["Content-Type"][0]) | 115 | ftype := fmt.Sprintf("%v", fheader.Header["Content-Type"][0]) |
116 | 116 | ||
117 | fextn := web.FileExtension(fname) | 117 | fextn := web.FileExtension(fname) |
118 | if fextn == "" { | 118 | if fextn == "" { |
119 | return doc, errors.New("invalid extension") | 119 | return doc, errors.New("invalid extension") |
120 | } | 120 | } |
121 | 121 | ||
122 | doc = new(Document) | 122 | doc = new(Document) |
123 | 123 | ||
124 | doc.FileName = fname | 124 | doc.FileName = fname |
125 | doc.Size = fsize | 125 | doc.Size = fsize |
126 | doc.ContentType = ftype | 126 | doc.ContentType = ftype |
127 | doc.Extension = "." + fextn | 127 | doc.Extension = "." + fextn |
128 | 128 | ||
129 | t := time.Now().Unix() | 129 | t := time.Now().Unix() |
130 | doc.TimeUploaded = t | 130 | doc.TimeUploaded = t |
131 | doc.TimeLastModified = t | 131 | doc.TimeLastModified = t |
132 | 132 | ||
133 | doc.UploadedBy = owner | 133 | doc.UploadedBy = owner |
134 | doc.LastModifiedBy = owner | 134 | doc.LastModifiedBy = owner |
135 | doc.RoleAccessLevel = 0 | 135 | doc.RoleAccessLevel = 0 |
136 | 136 | ||
137 | doc.data = make([]byte, doc.Size) | 137 | doc.data = make([]byte, doc.Size) |
138 | if _, err = io.ReadFull(file, doc.data); err != nil { | 138 | if _, err = io.ReadFull(file, doc.data); err != nil { |
139 | return doc, err | 139 | return doc, err |
140 | } | 140 | } |
141 | 141 | ||
142 | return doc, nil | 142 | return doc, nil |
143 | } | 143 | } |
144 | 144 | ||
145 | // SaveToFile ... | 145 | // SaveToFile ... |
146 | func (d *Document) SaveToFile(path string) (f *os.File, err error) { | 146 | func (d *Document) SaveToFile(path string) (f *os.File, err error) { |
147 | d.Path = path | 147 | d.Path = path |
148 | 148 | ||
149 | if web.FileExists(path) { | 149 | if web.FileExists(path) { |
150 | err = fmt.Errorf("file %s alredy exists", path) | 150 | err = fmt.Errorf("file %s alredy exists", path) |
151 | return nil, err | 151 | return nil, err |
152 | } | 152 | } |
153 | 153 | ||
154 | if parentDir := web.DirectoryFromPath(path); parentDir != "" { | 154 | if parentDir := web.DirectoryFromPath(path); parentDir != "" { |
155 | if err = os.MkdirAll(parentDir, os.ModePerm); err != nil { | 155 | if err = os.MkdirAll(parentDir, os.ModePerm); err != nil { |
156 | if !os.IsExist(err) { | 156 | if !os.IsExist(err) { |
157 | return nil, err | 157 | return nil, err |
158 | } | 158 | } |
159 | } | 159 | } |
160 | } | 160 | } |
161 | 161 | ||
162 | if f, err = os.Create(path); err != nil { | 162 | if f, err = os.Create(path); err != nil { |
163 | return nil, err | 163 | return nil, err |
164 | } | 164 | } |
165 | 165 | ||
166 | if _, err = f.Write(d.data); err != nil { | 166 | if _, err = f.Write(d.data); err != nil { |
167 | f.Close() | 167 | f.Close() |
168 | d.DeleteFile() | 168 | d.DeleteFile() |
169 | return nil, err | 169 | return nil, err |
170 | } | 170 | } |
171 | f.Close() | 171 | f.Close() |
172 | 172 | ||
173 | return f, nil | 173 | return f, nil |
174 | } | 174 | } |
175 | 175 | ||
176 | func DeleteDocuments(docs []*Document) error { | 176 | func DeleteDocuments(docs []*Document) error { |
177 | for _, d := range docs { | 177 | for _, d := range docs { |
178 | if d == nil { | ||
179 | continue | ||
180 | } | ||
178 | if err := d.DeleteFile(); err != nil { | 181 | if err := d.DeleteFile(); err != nil { |
179 | return err | 182 | return err |
180 | } | 183 | } |
181 | } | 184 | } |
182 | return nil | 185 | return nil |
183 | } | 186 | } |
184 | 187 | ||
185 | // DeleteFile ... | 188 | // DeleteFile ... |
186 | func (d *Document) DeleteFile() error { | 189 | func (d *Document) DeleteFile() error { |
187 | return os.Remove(d.Path) | 190 | return os.Remove(d.Path) |
188 | } | 191 | } |
189 | 192 |
quicksort.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | type QSortDirection int | 3 | type QSortDirection int |
4 | 4 | ||
5 | const ( | 5 | const ( |
6 | QSortAscending QSortDirection = iota | 6 | QSortAscending QSortDirection = iota |
7 | QSortDescending | 7 | QSortDescending |
8 | ) | 8 | ) |
9 | 9 | ||
10 | // QuickSortable is an interface for quicksorting slices. | 10 | // QuickSortable is an interface for quicksorting slices. |
11 | type QuickSortable interface { | 11 | type QuickSortable interface { |
12 | Swap(i, j int) | 12 | Swap(i, j int) |
13 | Compare(i, j int) int | 13 | Compare(i, j int) int |
14 | Len() int | 14 | Len() int |
15 | } | 15 | } |
16 | 16 | ||
17 | // Quicksort quicksorts que. | 17 | // Quicksort quicksorts que. |
18 | func Quicksort(que QuickSortable, low, high int, dir QSortDirection) { | 18 | func Quicksort(que QuickSortable, low, high int, dir QSortDirection) { |
19 | if low >= high { | 19 | if low >= high { |
20 | return | 20 | return |
21 | } | 21 | } |
22 | 22 | ||
23 | if dir != QSortAscending && dir != QSortDescending { | 23 | if dir != QSortAscending && dir != QSortDescending { |
24 | return | 24 | return |
25 | } | 25 | } |
26 | 26 | ||
27 | index := partition(que, low, high, dir) | 27 | index := partition(que, low, high, dir) |
28 | Quicksort(que, low, index-1, dir) | 28 | Quicksort(que, low, index-1, dir) |
29 | Quicksort(que, index+1, high, dir) | 29 | Quicksort(que, index+1, high, dir) |
30 | } | 30 | } |
31 | 31 | ||
32 | func partition(que QuickSortable, low, high int, dir QSortDirection) int { | 32 | func partition(que QuickSortable, low, high int, dir QSortDirection) int { |
33 | swap := 0 | 33 | swap := 0 |
34 | // -1 -> i > j | 34 | // -1 -> i > j |
35 | // 1 -> i < j | 35 | // 1 -> i < j |
36 | if dir == QSortDescending { | 36 | if dir == QSortDescending { |
37 | swap = -1 | 37 | swap = -1 |
38 | } else { | 38 | } else { |
39 | swap = 1 | 39 | swap = 1 |
40 | } | 40 | } |
41 | 41 | ||
42 | i := low - 1 | 42 | i := low - 1 |
43 | for j := low; j <= high-1; j++ { | 43 | for j := low; j <= high-1; j++ { |
44 | if que.Compare(j, high) == swap { | 44 | if que.Compare(j, high) == swap { |
45 | i++ | 45 | i++ |
46 | que.Swap(i, j) | 46 | que.Swap(i, j) |
47 | } | 47 | } |
48 | continue | 48 | continue |
49 | } | 49 | } |
50 | que.Swap(i+1, high) | 50 | que.Swap(i+1, high) |
51 | return i + 1 | 51 | return i + 1 |
52 | } | 52 | } |
53 | |||
54 | func BubbleSort(arr []int64) { | ||
55 | for i := 0; i < len(arr)-1; i++ { | ||
56 | for j := i; j < len(arr); j++ { | ||
57 | if arr[i] > arr[j] { | ||
58 | arr[i], arr[j] = arr[j], arr[i] | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | } | ||
53 | 63 |
server.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "database/sql" | 4 | "database/sql" |
5 | "fmt" | 5 | "fmt" |
6 | "net/http" | 6 | "net/http" |
7 | "time" | ||
8 | 7 | ||
9 | "git.to-net.rs/marko.tikvic/gologger" | 8 | "git.to-net.rs/marko.tikvic/gologger" |
10 | "github.com/gorilla/mux" | 9 | "github.com/gorilla/mux" |
11 | ) | 10 | ) |
12 | 11 | ||
13 | type Server struct { | 12 | type Server struct { |
14 | DB *sql.DB | 13 | DB *sql.DB |
15 | Router *mux.Router | 14 | Router *mux.Router |
16 | Logger *gologger.Logger | 15 | Logger *gologger.Logger |
17 | Port string | 16 | Port string |
18 | DBs map[string]*sql.DB | 17 | DBs map[string]*sql.DB |
19 | dsn map[string]string | 18 | dsn map[string]string |
20 | } | 19 | } |
21 | 20 | ||
22 | func NewODBCServer(dsn, port, logDir string) (s *Server, err error) { | 21 | func NewODBCServer(dsn, port, logDir string) (s *Server, err error) { |
23 | s = new(Server) | 22 | s = new(Server) |
24 | 23 | ||
25 | s.Port = port | 24 | s.Port = port |
26 | 25 | ||
27 | if s.DB, err = sql.Open("odbc", fmt.Sprintf("DSN=%s;", dsn)); err != nil { | 26 | if s.DB, err = sql.Open("odbc", fmt.Sprintf("DSN=%s;", dsn)); err != nil { |
28 | return nil, err | 27 | return nil, err |
29 | } | 28 | } |
30 | 29 | ||
31 | s.Router = mux.NewRouter() | 30 | s.Router = mux.NewRouter() |
32 | 31 | ||
33 | if s.Logger, err = gologger.New("err", logDir, gologger.MaxLogSize1MB); err != nil { | 32 | if s.Logger, err = gologger.New("err", logDir, gologger.MaxLogSize1MB); err != nil { |
34 | return nil, fmt.Errorf("can't create logger: %s", err.Error()) | 33 | return nil, fmt.Errorf("can't create logger: %s", err.Error()) |
35 | } | 34 | } |
36 | 35 | ||
37 | s.DBs = make(map[string]*sql.DB) | 36 | s.DBs = make(map[string]*sql.DB) |
38 | s.DBs["default"] = s.DB | 37 | s.DBs["default"] = s.DB |
39 | 38 | ||
40 | s.dsn = make(map[string]string) | 39 | s.dsn = make(map[string]string) |
41 | s.DBs["default"] = s.DB | 40 | s.dsn["default"] = dsn |
42 | 41 | ||
43 | return s, nil | 42 | return s, nil |
44 | } | 43 | } |
45 | 44 | ||
46 | func (s *Server) Run() { | 45 | func (s *Server) Run() { |
47 | s.Logger.Print("Server listening on %s", s.Port) | 46 | s.Logger.Print("Server listening on %s", s.Port) |
48 | s.Logger.PrintAndTrace(http.ListenAndServe(s.Port, s.Router).Error()) | 47 | s.Logger.PrintAndTrace(http.ListenAndServe(s.Port, s.Router).Error()) |
49 | } | 48 | } |
50 | 49 | ||
51 | func (s *Server) Cleanup() { | 50 | func (s *Server) Cleanup() { |
52 | if s.DB != nil { | 51 | if s.DB != nil { |
53 | s.DB.Close() | 52 | s.DB.Close() |
54 | } | 53 | } |
55 | 54 | ||
56 | if s.Logger != nil { | 55 | if s.Logger != nil { |
57 | s.Logger.Close() | 56 | s.Logger.Close() |
58 | } | 57 | } |
59 | } | 58 | } |
60 | 59 | ||
61 | func (s *Server) StartTransaction() (*sql.Tx, error) { | 60 | func (s *Server) StartTransaction() (*sql.Tx, error) { |
62 | return s.DB.Begin() | 61 | return s.DB.Begin() |
63 | } | 62 | } |
64 | 63 | ||
65 | func CommitChanges(tx *sql.Tx, err *error, opt ...error) { | 64 | func CommitChanges(tx *sql.Tx, err *error, opt ...error) { |
66 | if *err != nil { | 65 | if *err != nil { |
67 | tx.Rollback() | 66 | tx.Rollback() |
68 | return | 67 | return |
69 | } | 68 | } |
70 | 69 | ||
71 | for _, e := range opt { | 70 | for _, e := range opt { |
72 | if e != nil { | 71 | if e != nil { |
73 | tx.Rollback() | 72 | tx.Rollback() |
74 | return | 73 | return |
75 | } | 74 | } |
76 | } | 75 | } |
77 | 76 | ||
78 | if *err = tx.Commit(); *err != nil { | 77 | if *err = tx.Commit(); *err != nil { |
79 | tx.Rollback() | 78 | tx.Rollback() |
80 | } | 79 | } |
81 | } | 80 | } |
82 | |||
83 | func (s *Server) RefreshDatabaseConnections(period time.Duration) { | ||
84 | for { | ||
85 | for k, db := range s.DBs { | ||
86 | if err := db.Ping(); err != nil { | ||
87 | if s.Logger != nil { | ||
88 | s.Logger.PrintAndTrace("failed to ping database (%s): %s", s.dsn[k], err.Error()) | ||
89 | } else { | ||
90 | fmt.Println("failed to ping database (%s): %s", s.dsn[k], err.Error()) | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | time.Sleep(period) | ||
95 | } | ||
96 | } | ||
97 | 81 |