You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

299 lines
8.6 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
Git LFS support v2 (#122) * Import github.com/git-lfs/lfs-test-server as lfs module base Imported commit is 3968aac269a77b73924649b9412ae03f7ccd3198 Removed: Dockerfile CONTRIBUTING.md mgmt* script/ vendor/ kvlogger.go .dockerignore .gitignore README.md * Remove config, add JWT support from github.com/mgit-at/lfs-test-server Imported commit f0cdcc5a01599c5a955dc1bbf683bb4acecdba83 * Add LFS settings * Add LFS meta object model * Add LFS routes and initialization * Import github.com/dgrijalva/jwt-go into vendor/ * Adapt LFS module: handlers, routing, meta store * Move LFS routes to /user/repo/info/lfs/* * Add request header checks to LFS BatchHandler / PostHandler * Implement LFS basic authentication * Rework JWT secret generation / load * Implement LFS SSH token authentication with JWT Specification: https://github.com/github/git-lfs/tree/master/docs/api * Integrate LFS settings into install process * Remove LFS objects when repository is deleted Only removes objects from content store when deleted repo is the only referencing repository * Make LFS module stateless Fixes bug where LFS would not work after installation without restarting Gitea * Change 500 'Internal Server Error' to 400 'Bad Request' * Change sql query to xorm call * Remove unneeded type from LFS module * Change internal imports to code.gitea.io/gitea/ * Add Gitea authors copyright * Change basic auth realm to "gitea-lfs" * Add unique indexes to LFS model * Use xorm count function in LFS check on repository delete * Return io.ReadCloser from content store and close after usage * Add LFS info to runWeb() * Export LFS content store base path * LFS file download from UI * Work around git-lfs client issue with unauthenticated requests Returning a dummy Authorization header for unauthenticated requests lets git-lfs client skip asking for auth credentials See: https://github.com/github/git-lfs/issues/1088 * Fix unauthenticated UI downloads from public repositories * Authentication check order, Finish LFS file view logic * Ignore LFS hooks if installed for current OS user Fixes Gitea UI actions for repositories tracking LFS files. Checks for minimum needed git version by parsing the semantic version string. * Hide LFS metafile diff from commit view, marking as binary * Show LFS notice if file in commit view is tracked * Add notbefore/nbf JWT claim * Correct lint suggestions - comments for structs and functions - Add comments to LFS model - Function comment for GetRandomBytesAsBase64 - LFS server function comments and lint variable suggestion * Move secret generation code out of conditional Ensures no LFS code may run with an empty secret * Do not hand out JWT tokens if LFS server support is disabled
8 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
8 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "database/sql"
  7. "errors"
  8. "fmt"
  9. "net/url"
  10. "os"
  11. "path"
  12. "strings"
  13. // Needed for the MySQL driver
  14. _ "github.com/go-sql-driver/mysql"
  15. "github.com/go-xorm/core"
  16. "github.com/go-xorm/xorm"
  17. // Needed for the Postgresql driver
  18. _ "github.com/lib/pq"
  19. // Needed for the MSSSQL driver
  20. _ "github.com/denisenkom/go-mssqldb"
  21. "code.gitea.io/gitea/models/migrations"
  22. "code.gitea.io/gitea/modules/setting"
  23. )
  24. // Engine represents a xorm engine or session.
  25. type Engine interface {
  26. Delete(interface{}) (int64, error)
  27. Exec(string, ...interface{}) (sql.Result, error)
  28. Find(interface{}, ...interface{}) error
  29. Get(interface{}) (bool, error)
  30. Id(interface{}) *xorm.Session
  31. In(string, ...interface{}) *xorm.Session
  32. Insert(...interface{}) (int64, error)
  33. InsertOne(interface{}) (int64, error)
  34. Iterate(interface{}, xorm.IterFunc) error
  35. SQL(interface{}, ...interface{}) *xorm.Session
  36. Where(interface{}, ...interface{}) *xorm.Session
  37. }
  38. func sessionRelease(sess *xorm.Session) {
  39. if !sess.IsCommitedOrRollbacked {
  40. sess.Rollback()
  41. }
  42. sess.Close()
  43. }
  44. var (
  45. x *xorm.Engine
  46. tables []interface{}
  47. // HasEngine specifies if we have a xorm.Engine
  48. HasEngine bool
  49. // DbCfg holds the database settings
  50. DbCfg struct {
  51. Type, Host, Name, User, Passwd, Path, SSLMode string
  52. }
  53. // EnableSQLite3 use SQLite3
  54. EnableSQLite3 bool
  55. // EnableTiDB enable TiDB
  56. EnableTiDB bool
  57. )
  58. func init() {
  59. tables = append(tables,
  60. new(User), new(PublicKey), new(AccessToken),
  61. new(Repository), new(DeployKey), new(Collaboration), new(Access), new(Upload),
  62. new(Watch), new(Star), new(Follow), new(Action),
  63. new(Issue), new(PullRequest), new(Comment), new(Attachment), new(IssueUser),
  64. new(Label), new(IssueLabel), new(Milestone),
  65. new(Mirror), new(Release), new(LoginSource), new(Webhook),
  66. new(UpdateTask), new(HookTask),
  67. new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
  68. new(Notice), new(EmailAddress), new(LFSMetaObject))
  69. gonicNames := []string{"SSL", "UID"}
  70. for _, name := range gonicNames {
  71. core.LintGonicMapper[name] = true
  72. }
  73. }
  74. // LoadConfigs loads the database settings
  75. func LoadConfigs() {
  76. sec := setting.Cfg.Section("database")
  77. DbCfg.Type = sec.Key("DB_TYPE").String()
  78. switch DbCfg.Type {
  79. case "sqlite3":
  80. setting.UseSQLite3 = true
  81. case "mysql":
  82. setting.UseMySQL = true
  83. case "postgres":
  84. setting.UsePostgreSQL = true
  85. case "tidb":
  86. setting.UseTiDB = true
  87. case "mssql":
  88. setting.UseMSSQL = true
  89. }
  90. DbCfg.Host = sec.Key("HOST").String()
  91. DbCfg.Name = sec.Key("NAME").String()
  92. DbCfg.User = sec.Key("USER").String()
  93. if len(DbCfg.Passwd) == 0 {
  94. DbCfg.Passwd = sec.Key("PASSWD").String()
  95. }
  96. DbCfg.SSLMode = sec.Key("SSL_MODE").String()
  97. DbCfg.Path = sec.Key("PATH").MustString("data/gitea.db")
  98. }
  99. // parsePostgreSQLHostPort parses given input in various forms defined in
  100. // https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
  101. // and returns proper host and port number.
  102. func parsePostgreSQLHostPort(info string) (string, string) {
  103. host, port := "127.0.0.1", "5432"
  104. if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
  105. idx := strings.LastIndex(info, ":")
  106. host = info[:idx]
  107. port = info[idx+1:]
  108. } else if len(info) > 0 {
  109. host = info
  110. }
  111. return host, port
  112. }
  113. func parseMSSQLHostPort(info string) (string, string) {
  114. host, port := "127.0.0.1", "1433"
  115. if strings.Contains(info, ":") {
  116. host = strings.Split(info, ":")[0]
  117. port = strings.Split(info, ":")[1]
  118. } else if strings.Contains(info, ",") {
  119. host = strings.Split(info, ",")[0]
  120. port = strings.TrimSpace(strings.Split(info, ",")[1])
  121. } else if len(info) > 0 {
  122. host = info
  123. }
  124. return host, port
  125. }
  126. func getEngine() (*xorm.Engine, error) {
  127. connStr := ""
  128. var Param = "?"
  129. if strings.Contains(DbCfg.Name, Param) {
  130. Param = "&"
  131. }
  132. switch DbCfg.Type {
  133. case "mysql":
  134. if DbCfg.Host[0] == '/' { // looks like a unix socket
  135. connStr = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8&parseTime=true",
  136. DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
  137. } else {
  138. connStr = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8&parseTime=true",
  139. DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
  140. }
  141. case "postgres":
  142. host, port := parsePostgreSQLHostPort(DbCfg.Host)
  143. if host[0] == '/' { // looks like a unix socket
  144. connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s",
  145. url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), port, DbCfg.Name, Param, DbCfg.SSLMode, host)
  146. } else {
  147. connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
  148. url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), host, port, DbCfg.Name, Param, DbCfg.SSLMode)
  149. }
  150. case "mssql":
  151. host, port := parseMSSQLHostPort(DbCfg.Host)
  152. connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, DbCfg.Name, DbCfg.User, DbCfg.Passwd)
  153. case "sqlite3":
  154. if !EnableSQLite3 {
  155. return nil, errors.New("this binary version does not build support for SQLite3")
  156. }
  157. if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
  158. return nil, fmt.Errorf("Fail to create directories: %v", err)
  159. }
  160. connStr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
  161. case "tidb":
  162. if !EnableTiDB {
  163. return nil, errors.New("this binary version does not build support for TiDB")
  164. }
  165. if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
  166. return nil, fmt.Errorf("Fail to create directories: %v", err)
  167. }
  168. connStr = "goleveldb://" + DbCfg.Path
  169. default:
  170. return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
  171. }
  172. return xorm.NewEngine(DbCfg.Type, connStr)
  173. }
  174. // NewTestEngine sets a new test xorm.Engine
  175. func NewTestEngine(x *xorm.Engine) (err error) {
  176. x, err = getEngine()
  177. if err != nil {
  178. return fmt.Errorf("Connect to database: %v", err)
  179. }
  180. x.SetMapper(core.GonicMapper{})
  181. return x.StoreEngine("InnoDB").Sync2(tables...)
  182. }
  183. // SetEngine sets the xorm.Engine
  184. func SetEngine() (err error) {
  185. x, err = getEngine()
  186. if err != nil {
  187. return fmt.Errorf("Fail to connect to database: %v", err)
  188. }
  189. x.SetMapper(core.GonicMapper{})
  190. // WARNING: for serv command, MUST remove the output to os.stdout,
  191. // so use log file to instead print to stdout.
  192. logPath := path.Join(setting.LogRootPath, "xorm.log")
  193. if err := os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
  194. return fmt.Errorf("Fail to create dir %s: %v", logPath, err)
  195. }
  196. f, err := os.Create(logPath)
  197. if err != nil {
  198. return fmt.Errorf("Fail to create xorm.log: %v", err)
  199. }
  200. x.SetLogger(xorm.NewSimpleLogger(f))
  201. x.ShowSQL(true)
  202. return nil
  203. }
  204. // NewEngine initializes a new xorm.Engine
  205. func NewEngine() (err error) {
  206. if err = SetEngine(); err != nil {
  207. return err
  208. }
  209. if err = x.Ping(); err != nil {
  210. return err
  211. }
  212. if err = migrations.Migrate(x); err != nil {
  213. return fmt.Errorf("migrate: %v", err)
  214. }
  215. if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
  216. return fmt.Errorf("sync database struct error: %v", err)
  217. }
  218. return nil
  219. }
  220. // Statistic contains the database statistics
  221. type Statistic struct {
  222. Counter struct {
  223. User, Org, PublicKey,
  224. Repo, Watch, Star, Action, Access,
  225. Issue, Comment, Oauth, Follow,
  226. Mirror, Release, LoginSource, Webhook,
  227. Milestone, Label, HookTask,
  228. Team, UpdateTask, Attachment int64
  229. }
  230. }
  231. // GetStatistic returns the database statistics
  232. func GetStatistic() (stats Statistic) {
  233. stats.Counter.User = CountUsers()
  234. stats.Counter.Org = CountOrganizations()
  235. stats.Counter.PublicKey, _ = x.Count(new(PublicKey))
  236. stats.Counter.Repo = CountRepositories(true)
  237. stats.Counter.Watch, _ = x.Count(new(Watch))
  238. stats.Counter.Star, _ = x.Count(new(Star))
  239. stats.Counter.Action, _ = x.Count(new(Action))
  240. stats.Counter.Access, _ = x.Count(new(Access))
  241. stats.Counter.Issue, _ = x.Count(new(Issue))
  242. stats.Counter.Comment, _ = x.Count(new(Comment))
  243. stats.Counter.Oauth = 0
  244. stats.Counter.Follow, _ = x.Count(new(Follow))
  245. stats.Counter.Mirror, _ = x.Count(new(Mirror))
  246. stats.Counter.Release, _ = x.Count(new(Release))
  247. stats.Counter.LoginSource = CountLoginSources()
  248. stats.Counter.Webhook, _ = x.Count(new(Webhook))
  249. stats.Counter.Milestone, _ = x.Count(new(Milestone))
  250. stats.Counter.Label, _ = x.Count(new(Label))
  251. stats.Counter.HookTask, _ = x.Count(new(HookTask))
  252. stats.Counter.Team, _ = x.Count(new(Team))
  253. stats.Counter.UpdateTask, _ = x.Count(new(UpdateTask))
  254. stats.Counter.Attachment, _ = x.Count(new(Attachment))
  255. return
  256. }
  257. // Ping tests if database is alive
  258. func Ping() error {
  259. return x.Ping()
  260. }
  261. // DumpDatabase dumps all data from database to file system.
  262. func DumpDatabase(filePath string) error {
  263. return x.DumpAllToFile(filePath)
  264. }