Compare commits

..

3 Commits

18 changed files with 253 additions and 242 deletions
+5 -5
View File
@@ -1042,8 +1042,8 @@ func (s *Store) ListSSHAccessProfilesForUser(userID string) ([]models.SSHAccessP
p.owner_scope = ? p.owner_scope = ?
AND p.enabled = 1 AND p.enabled = 1
AND s.enabled = 1 AND s.enabled = 1
AND (p.valid_after = 0 OR p.valid_after <= strftime('%s','now')) AND (p.valid_after = 0 OR p.valid_after <= CAST(strftime('%s','now') AS INTEGER))
AND (p.valid_before = 0 OR p.valid_before >= strftime('%s','now')) AND (p.valid_before = 0 OR p.valid_before >= CAST(strftime('%s','now') AS INTEGER))
AND ( AND (
(t.target_type = 'user' AND tu.public_id = ?) (t.target_type = 'user' AND tu.public_id = ?)
OR OR
@@ -1234,9 +1234,9 @@ func (s *Store) GetSSHAccessProfileForUser(userID string, id string) (models.SSH
p.owner_scope = ? p.owner_scope = ?
AND p.enabled = 1 AND p.enabled = 1
AND s.enabled = 1 AND s.enabled = 1
AND (p.valid_after = 0 OR p.valid_after <= strftime('%s','now')) AND (p.valid_after = 0 OR p.valid_after <= CAST(strftime('%s','now') AS INTEGER))
AND (p.valid_before = 0 OR p.valid_before >= strftime('%s','now')) AND (p.valid_before = 0 OR p.valid_before >= CAST(strftime('%s','now') AS INTEGER))
AND ( AND (
(t.target_type = 'user' AND tu.public_id = ?) (t.target_type = 'user' AND tu.public_id = ?)
OR OR
(t.target_type = 'group' AND ug.disabled = 0 AND (ug.scope = 'all_users' OR gu.public_id = ?)) (t.target_type = 'group' AND ug.disabled = 0 AND (ug.scope = 'all_users' OR gu.public_id = ?))
+68 -155
View File
@@ -11,7 +11,6 @@ import "codit/internal/util"
func (s *Store) CreateUser(user models.User, passwordHash string) (models.User, error) { func (s *Store) CreateUser(user models.User, passwordHash string) (models.User, error) {
var id string var id string
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
if user.ID == "" { if user.ID == "" {
id, err = util.NewID() id, err = util.NewID()
@@ -20,44 +19,39 @@ func (s *Store) CreateUser(user models.User, passwordHash string) (models.User,
} }
user.ID = id user.ID = id
} }
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
user.CreatedAt = nowUnix user.CreatedAt = nowUnix
user.UpdatedAt = nowUnix user.UpdatedAt = nowUnix
_, err = s.Exec(` _, err = s.Exec(`
INSERT INTO users (public_id, username, display_name, email, password_hash, is_admin, disabled, auth_provider_id, totp_required, external_subject, created_at, updated_at) INSERT INTO users (public_id, username, display_name, email, password_hash, is_admin, disabled, auth_provider_id, totp_required, external_subject, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, CASE WHEN ? = '' THEN NULL ELSE (SELECT id FROM auth_providers WHERE public_id = ?) END, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, CASE WHEN ? = '' THEN NULL ELSE (SELECT id FROM auth_providers WHERE public_id = ?) END, ?, ?, ?, ?)
`, user.ID, user.Username, user.DisplayName, user.Email, passwordHash, user.IsAdmin, user.Disabled, user.AuthProviderID, user.AuthProviderID, user.TOTPRequired, user.ExternalSubject, now, now) `, user.ID, user.Username, user.DisplayName, user.Email, passwordHash, user.IsAdmin, user.Disabled, user.AuthProviderID, user.AuthProviderID, user.TOTPRequired, user.ExternalSubject, nowUnix, nowUnix)
return user, err return user, err
} }
func (s *Store) UpdateUser(user models.User) error { func (s *Store) UpdateUser(user models.User) error {
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
user.UpdatedAt = nowUnix user.UpdatedAt = nowUnix
_, err = s.Exec(`UPDATE users SET display_name = ?, email = ?, is_admin = ?, disabled = ?, totp_required = ?, updated_at = ? WHERE public_id = ?`, _, err = s.Exec(`UPDATE users SET display_name = ?, email = ?, is_admin = ?, disabled = ?, totp_required = ?, updated_at = ? WHERE public_id = ?`,
user.DisplayName, user.Email, user.IsAdmin, user.Disabled, user.TOTPRequired, now, user.ID) user.DisplayName, user.Email, user.IsAdmin, user.Disabled, user.TOTPRequired, nowUnix, user.ID)
return err return err
} }
func (s *Store) UpdateUserWithPassword(user models.User, passwordHash string) error { func (s *Store) UpdateUserWithPassword(user models.User, passwordHash string) error {
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
var tx *sql.Tx var tx *sql.Tx
var owned bool var owned bool
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
user.UpdatedAt = nowUnix user.UpdatedAt = nowUnix
tx, owned, err = s.begin() tx, owned, err = s.begin()
if err != nil { if err != nil {
return err return err
} }
_, err = tx.Exec(`UPDATE users SET display_name = ?, email = ?, is_admin = ?, disabled = ?, totp_required = ?, password_hash = ?, updated_at = ? WHERE public_id = ?`, _, err = tx.Exec(`UPDATE users SET display_name = ?, email = ?, is_admin = ?, disabled = ?, totp_required = ?, password_hash = ?, updated_at = ? WHERE public_id = ?`,
user.DisplayName, user.Email, user.IsAdmin, user.Disabled, user.TOTPRequired, passwordHash, now, user.ID) user.DisplayName, user.Email, user.IsAdmin, user.Disabled, user.TOTPRequired, passwordHash, nowUnix, user.ID)
if err != nil { if err != nil {
rollbackIfOwned(tx, owned) rollbackIfOwned(tx, owned)
return err return err
@@ -71,23 +65,19 @@ func (s *Store) UpdateUserWithPassword(user models.User, passwordHash string) er
func (s *Store) SetUserPassword(userID, passwordHash string) error { func (s *Store) SetUserPassword(userID, passwordHash string) error {
var err error var err error
_, err = s.Exec(`UPDATE users SET password_hash = ?, updated_at = ? WHERE public_id = ?`, passwordHash, time.Now().UTC(), userID) _, err = s.Exec(`UPDATE users SET password_hash = ?, updated_at = ? WHERE public_id = ?`, passwordHash, time.Now().UTC().Unix(), userID)
return err return err
} }
func (s *Store) GetUserByID(id string) (models.User, error) { func (s *Store) GetUserByID(id string) (models.User, error) {
var user models.User var user models.User
var row *sql.Row var row *sql.Row
var created time.Time
var updated time.Time
var err error var err error
row = s.QueryRow(`SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), COALESCE(t.enabled, 0), u.totp_required, u.created_at, u.updated_at FROM users u LEFT JOIN auth_providers ap ON ap.id = u.auth_provider_id LEFT JOIN user_totp t ON t.user_id = u.id WHERE u.public_id = ?`, id) row = s.QueryRow(`SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), COALESCE(t.enabled, 0), u.totp_required, u.created_at, u.updated_at FROM users u LEFT JOIN auth_providers ap ON ap.id = u.auth_provider_id LEFT JOIN user_totp t ON t.user_id = u.id WHERE u.public_id = ?`, id)
err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled, &user.AuthProviderID, &user.TOTPEnabled, &user.TOTPRequired, &created, &updated) err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled, &user.AuthProviderID, &user.TOTPEnabled, &user.TOTPRequired, &user.CreatedAt, &user.UpdatedAt)
if err != nil { if err != nil {
return user, err return user, err
} }
user.CreatedAt = created.Unix()
user.UpdatedAt = updated.Unix()
return user, nil return user, nil
} }
@@ -96,15 +86,11 @@ func (s *Store) GetUserByUsername(username string) (models.User, string, error)
var passwordHash sql.NullString var passwordHash sql.NullString
var row *sql.Row var row *sql.Row
var err error var err error
var created time.Time
var updated time.Time
row = s.QueryRow(`SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), u.password_hash, COALESCE(t.enabled, 0), u.totp_required, u.created_at, u.updated_at FROM users u LEFT JOIN auth_providers ap ON ap.id = u.auth_provider_id LEFT JOIN user_totp t ON t.user_id = u.id WHERE u.username = ?`, username) row = s.QueryRow(`SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), u.password_hash, COALESCE(t.enabled, 0), u.totp_required, u.created_at, u.updated_at FROM users u LEFT JOIN auth_providers ap ON ap.id = u.auth_provider_id LEFT JOIN user_totp t ON t.user_id = u.id WHERE u.username = ?`, username)
err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled, &user.AuthProviderID, &passwordHash, &user.TOTPEnabled, &user.TOTPRequired, &created, &updated) err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled, &user.AuthProviderID, &passwordHash, &user.TOTPEnabled, &user.TOTPRequired, &user.CreatedAt, &user.UpdatedAt)
if err != nil { if err != nil {
return user, passwordHash.String, err return user, passwordHash.String, err
} }
user.CreatedAt = created.Unix()
user.UpdatedAt = updated.Unix()
return user, passwordHash.String, nil return user, passwordHash.String, nil
} }
@@ -113,20 +99,16 @@ func (s *Store) ListUsers() ([]models.User, error) {
var err error var err error
var users []models.User var users []models.User
var u models.User var u models.User
var created time.Time
var updated time.Time
rows, err = s.Query(`SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), COALESCE(t.enabled, 0), u.totp_required, u.created_at, u.updated_at FROM users u LEFT JOIN auth_providers ap ON ap.id = u.auth_provider_id LEFT JOIN user_totp t ON t.user_id = u.id ORDER BY u.username`) rows, err = s.Query(`SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), COALESCE(t.enabled, 0), u.totp_required, u.created_at, u.updated_at FROM users u LEFT JOIN auth_providers ap ON ap.id = u.auth_provider_id LEFT JOIN user_totp t ON t.user_id = u.id ORDER BY u.username`)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err = rows.Scan(&u.ID, &u.Username, &u.DisplayName, &u.Email, &u.IsAdmin, &u.Disabled, &u.AuthProviderID, &u.TOTPEnabled, &u.TOTPRequired, &created, &updated) err = rows.Scan(&u.ID, &u.Username, &u.DisplayName, &u.Email, &u.IsAdmin, &u.Disabled, &u.AuthProviderID, &u.TOTPEnabled, &u.TOTPRequired, &u.CreatedAt, &u.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
u.CreatedAt = created.Unix()
u.UpdatedAt = updated.Unix()
users = append(users, u) users = append(users, u)
} }
return users, rows.Err() return users, rows.Err()
@@ -167,8 +149,6 @@ func (s *Store) DeleteUser(id string) error {
func (s *Store) GetUserByProviderAndSub(providerPublicID string, sub string) (models.User, error) { func (s *Store) GetUserByProviderAndSub(providerPublicID string, sub string) (models.User, error) {
var user models.User var user models.User
var row *sql.Row var row *sql.Row
var created time.Time
var updated time.Time
var err error var err error
row = s.QueryRow(` row = s.QueryRow(`
SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled,
@@ -179,12 +159,10 @@ func (s *Store) GetUserByProviderAndSub(providerPublicID string, sub string) (mo
WHERE ap.public_id = ? AND u.external_subject = ? WHERE ap.public_id = ? AND u.external_subject = ?
`, strings.TrimSpace(providerPublicID), strings.TrimSpace(sub)) `, strings.TrimSpace(providerPublicID), strings.TrimSpace(sub))
err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled, err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled,
&user.AuthProviderID, &user.ExternalSubject, &user.TOTPEnabled, &user.TOTPRequired, &created, &updated) &user.AuthProviderID, &user.ExternalSubject, &user.TOTPEnabled, &user.TOTPRequired, &user.CreatedAt, &user.UpdatedAt)
if err != nil { if err != nil {
return user, err return user, err
} }
user.CreatedAt = created.Unix()
user.UpdatedAt = updated.Unix()
return user, nil return user, nil
} }
@@ -368,24 +346,20 @@ func splitCSVValue(value string) []string {
func (s *Store) SetUserDisabled(id string, disabled bool) error { func (s *Store) SetUserDisabled(id string, disabled bool) error {
var err error var err error
var now time.Time _, err = s.Exec(`UPDATE users SET disabled = ?, updated_at = ? WHERE public_id = ?`, disabled, time.Now().UTC().Unix(), id)
now = time.Now().UTC()
_, err = s.Exec(`UPDATE users SET disabled = ?, updated_at = ? WHERE public_id = ?`, disabled, now, id)
return err return err
} }
func (s *Store) CreateAPIKey(userID string, name string, tokenHash string, prefix string, expiresAt int64) (models.APIKey, error) { func (s *Store) CreateAPIKey(userID string, name string, tokenHash string, prefix string, expiresAt int64) (models.APIKey, error) {
var key models.APIKey var key models.APIKey
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
var id string var id string
id, err = util.NewID() id, err = util.NewID()
if err != nil { if err != nil {
return key, err return key, err
} }
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
key = models.APIKey{ key = models.APIKey{
ID: id, ID: id,
UserID: userID, UserID: userID,
@@ -604,19 +578,14 @@ func (s *Store) GetUserByAPIKeyHash(tokenHash string) (models.User, error) {
var tx *sql.Tx var tx *sql.Tx
var owned bool var owned bool
var row *sql.Row var row *sql.Row
var created time.Time
var updated time.Time
var keyID int64 var keyID int64
var now time.Time
var nowUnix int64 var nowUnix int64
var err error var err error
var currentUnix int64
tx, owned, err = s.begin() tx, owned, err = s.begin()
if err != nil { if err != nil {
return user, err return user, err
} }
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
currentUnix = now.Unix()
row = tx.QueryRow(` row = tx.QueryRow(`
SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), u.created_at, u.updated_at, k.id SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), u.created_at, u.updated_at, k.id
FROM api_keys k FROM api_keys k
@@ -627,16 +596,13 @@ func (s *Store) GetUserByAPIKeyHash(tokenHash string) (models.User, error) {
AND k.disabled = 0 AND k.disabled = 0
AND (k.expires_at = 0 OR k.expires_at > ?) AND (k.expires_at = 0 OR k.expires_at > ?)
LIMIT 1 LIMIT 1
`, tokenHash, currentUnix) `, tokenHash, nowUnix)
err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled, &user.AuthProviderID, &created, &updated, &keyID) err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled, &user.AuthProviderID, &user.CreatedAt, &user.UpdatedAt, &keyID)
if err != nil { if err != nil {
rollbackIfOwned(tx, owned) rollbackIfOwned(tx, owned)
return user, err return user, err
} }
user.CreatedAt = created.Unix() nowUnix = time.Now().UTC().Unix()
user.UpdatedAt = updated.Unix()
now = time.Now().UTC()
nowUnix = now.Unix()
_, err = tx.Exec(`UPDATE api_keys SET last_used_at = ? WHERE id = ?`, nowUnix, keyID) _, err = tx.Exec(`UPDATE api_keys SET last_used_at = ? WHERE id = ?`, nowUnix, keyID)
if err != nil { if err != nil {
rollbackIfOwned(tx, owned) rollbackIfOwned(tx, owned)
@@ -653,7 +619,7 @@ func (s *Store) CreateSession(userID string, token string, expiresAt time.Time,
var err error var err error
_, err = s.Exec(`INSERT INTO sessions (id, user_id, token, expires_at, created_at, totp_verified) _, err = s.Exec(`INSERT INTO sessions (id, user_id, token, expires_at, created_at, totp_verified)
VALUES (?, (SELECT id FROM users WHERE public_id = ?), ?, ?, ?, ?)`, VALUES (?, (SELECT id FROM users WHERE public_id = ?), ?, ?, ?, ?)`,
mustID(), userID, token, expiresAt, time.Now().UTC(), totpVerified) mustID(), userID, token, expiresAt.Unix(), time.Now().UTC().Unix(), totpVerified)
return err return err
} }
@@ -671,18 +637,16 @@ func (s *Store) DeleteSession(token string) error {
func (s *Store) DeleteExpiredSessions(now time.Time) error { func (s *Store) DeleteExpiredSessions(now time.Time) error {
var err error var err error
_, err = s.Exec(`DELETE FROM sessions WHERE expires_at < ?`, now.UTC()) _, err = s.Exec(`DELETE FROM sessions WHERE expires_at < ?`, now.UTC().Unix())
return err return err
} }
func (s *Store) GetSessionUser(token string) (models.User, time.Time, bool, error) { func (s *Store) GetSessionUser(token string) (models.User, time.Time, bool, error) {
var user models.User var user models.User
var expires time.Time var expiresUnix int64
var totpVerified bool var totpVerified bool
var row *sql.Row var row *sql.Row
var err error var err error
var created time.Time
var updated time.Time
row = s.QueryRow(` row = s.QueryRow(`
SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), COALESCE(t.enabled, 0), u.totp_required, SELECT u.public_id, u.username, u.display_name, u.email, u.is_admin, u.disabled, COALESCE(ap.public_id, ''), COALESCE(t.enabled, 0), u.totp_required,
(u.totp_required OR EXISTS ( (u.totp_required OR EXISTS (
@@ -698,20 +662,17 @@ func (s *Store) GetSessionUser(token string) (models.User, time.Time, bool, erro
LEFT JOIN auth_providers ap ON ap.id = u.auth_provider_id LEFT JOIN auth_providers ap ON ap.id = u.auth_provider_id
WHERE s.token = ? AND u.disabled = 0 WHERE s.token = ? AND u.disabled = 0
`, token) `, token)
err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled, &user.AuthProviderID, &user.TOTPEnabled, &user.TOTPRequired, &user.TOTPEffectiveRequired, &totpVerified, &created, &updated, &expires) err = row.Scan(&user.ID, &user.Username, &user.DisplayName, &user.Email, &user.IsAdmin, &user.Disabled, &user.AuthProviderID, &user.TOTPEnabled, &user.TOTPRequired, &user.TOTPEffectiveRequired, &totpVerified, &user.CreatedAt, &user.UpdatedAt, &expiresUnix)
if err != nil { if err != nil {
return user, time.Time{}, false, err return user, time.Time{}, false, err
} }
user.CreatedAt = created.Unix()
user.UpdatedAt = updated.Unix()
user.TOTPSetupRequired = user.TOTPEffectiveRequired && !user.TOTPEnabled user.TOTPSetupRequired = user.TOTPEffectiveRequired && !user.TOTPEnabled
return user, expires, totpVerified, nil return user, time.Unix(expiresUnix, 0).UTC(), totpVerified, nil
} }
func (s *Store) CreateProject(project models.Project) (models.Project, error) { func (s *Store) CreateProject(project models.Project) (models.Project, error) {
var id string var id string
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
var tx *sql.Tx var tx *sql.Tx
var owned bool var owned bool
@@ -722,8 +683,7 @@ func (s *Store) CreateProject(project models.Project) (models.Project, error) {
} }
project.ID = id project.ID = id
} }
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
project.CreatedAt = nowUnix project.CreatedAt = nowUnix
project.UpdatedAt = nowUnix project.UpdatedAt = nowUnix
if project.UpdatedBy == "" { if project.UpdatedBy == "" {
@@ -734,10 +694,10 @@ func (s *Store) CreateProject(project models.Project) (models.Project, error) {
return project, err return project, err
} }
if project.HomePage == "" { if project.HomePage == "" {
project.HomePage = "info" project.HomePage = "settings"
} }
_, err = tx.Exec(`INSERT INTO projects (public_id, slug, name, description, home_page, created_by, updated_by, created_at, updated_at, created_at_unix, updated_at_unix) _, err = tx.Exec(`INSERT INTO projects (public_id, slug, name, description, home_page, created_by, updated_by, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), (SELECT id FROM users WHERE public_id = ?), ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), (SELECT id FROM users WHERE public_id = ?), ?, ?)`,
project.ID, project.ID,
project.Slug, project.Slug,
project.Name, project.Name,
@@ -745,8 +705,6 @@ func (s *Store) CreateProject(project models.Project) (models.Project, error) {
project.HomePage, project.HomePage,
project.CreatedBy, project.CreatedBy,
project.UpdatedBy, project.UpdatedBy,
now,
now,
project.CreatedAt, project.CreatedAt,
project.UpdatedAt, project.UpdatedAt,
) )
@@ -771,16 +729,15 @@ func (s *Store) CreateProject(project models.Project) (models.Project, error) {
func (s *Store) UpdateProject(project models.Project) error { func (s *Store) UpdateProject(project models.Project) error {
var err error var err error
if project.HomePage == "" { if project.HomePage == "" {
project.HomePage = "info" project.HomePage = "settings"
} }
_, err = s.Exec(`UPDATE projects SET slug = ?, name = ?, description = ?, home_page = ?, updated_at = ?, updated_by = (SELECT id FROM users WHERE public_id = ?), updated_at_unix = ? WHERE public_id = ?`, _, err = s.Exec(`UPDATE projects SET slug = ?, name = ?, description = ?, home_page = ?, updated_at = ?, updated_by = (SELECT id FROM users WHERE public_id = ?) WHERE public_id = ?`,
project.Slug, project.Slug,
project.Name, project.Name,
project.Description, project.Description,
project.HomePage, project.HomePage,
time.Unix(project.UpdatedAt, 0).UTC(),
project.UpdatedBy,
project.UpdatedAt, project.UpdatedAt,
project.UpdatedBy,
project.ID, project.ID,
) )
return err return err
@@ -793,7 +750,7 @@ func (s *Store) GetProject(id string) (models.Project, error) {
SELECT p.public_id, p.slug, p.name, p.description, p.home_page, SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -822,7 +779,7 @@ func (s *Store) GetProjectBySlug(slug string) (models.Project, error) {
SELECT p.public_id, p.slug, p.name, p.description, p.home_page, SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -856,7 +813,7 @@ func (s *Store) ListProjects() ([]models.Project, error) {
SELECT p.public_id, p.slug, p.name, p.description, p.home_page, SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -897,7 +854,7 @@ func (s *Store) ListProjectsForUser(userID string) ([]models.Project, error) {
SELECT p.public_id, p.slug, p.name, p.description, p.home_page, SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -962,7 +919,7 @@ func (s *Store) ListProjectsForPrincipal(principalID string) ([]models.Project,
SELECT p.public_id, p.slug, p.name, p.description, p.home_page, SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -1017,7 +974,7 @@ func (s *Store) ListProjectsFiltered(limit int, offset int, query string) ([]mod
`SELECT p.public_id, p.slug, p.name, p.description, p.home_page, `SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -1030,7 +987,7 @@ func (s *Store) ListProjectsFiltered(limit int, offset int, query string) ([]mod
`SELECT p.public_id, p.slug, p.name, p.description, p.home_page, `SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -1084,7 +1041,7 @@ func (s *Store) ListProjectsFilteredForUser(userID string, limit int, offset int
`SELECT p.public_id, p.slug, p.name, p.description, p.home_page, `SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -1122,7 +1079,7 @@ func (s *Store) ListProjectsFilteredForUser(userID string, limit int, offset int
`SELECT p.public_id, p.slug, p.name, p.description, p.home_page, `SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -1201,7 +1158,7 @@ func (s *Store) ListProjectsFilteredForPrincipal(principalID string, limit int,
`SELECT p.public_id, p.slug, p.name, p.description, p.home_page, `SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -1222,7 +1179,7 @@ func (s *Store) ListProjectsFilteredForPrincipal(principalID string, limit int,
`SELECT p.public_id, p.slug, p.name, p.description, p.home_page, `SELECT p.public_id, p.slug, p.name, p.description, p.home_page,
c.public_id, u.public_id, c.public_id, u.public_id,
COALESCE(c.username, ''), COALESCE(u.username, ''), COALESCE(c.username, ''), COALESCE(u.username, ''),
p.created_at_unix, p.updated_at_unix p.created_at, p.updated_at
FROM projects p FROM projects p
LEFT JOIN users c ON c.id = p.created_by LEFT JOIN users c ON c.id = p.created_by
LEFT JOIN users u ON u.id = p.updated_by LEFT JOIN users u ON u.id = p.updated_by
@@ -1497,7 +1454,6 @@ func (s *Store) GetProjectMemberRole(projectID, userID string) (string, error) {
func (s *Store) CreateRepo(repo models.Repo) (models.Repo, error) { func (s *Store) CreateRepo(repo models.Repo) (models.Repo, error) {
var id string var id string
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
if repo.ID == "" { if repo.ID == "" {
id, err = util.NewID() id, err = util.NewID()
@@ -1506,12 +1462,11 @@ func (s *Store) CreateRepo(repo models.Repo) (models.Repo, error) {
} }
repo.ID = id repo.ID = id
} }
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
repo.CreatedAt = nowUnix repo.CreatedAt = nowUnix
_, err = s.Exec(`INSERT INTO repos (public_id, project_id, name, type, path, created_by, created_at) _, err = s.Exec(`INSERT INTO repos (public_id, project_id, name, type, path, created_by, created_at)
VALUES (?, (SELECT id FROM projects WHERE public_id = ?), ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), ?)`, VALUES (?, (SELECT id FROM projects WHERE public_id = ?), ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), ?)`,
repo.ID, repo.ProjectID, repo.Name, repo.Type, repo.Path, repo.CreatedBy, now) repo.ID, repo.ProjectID, repo.Name, repo.Type, repo.Path, repo.CreatedBy, nowUnix)
return repo, err return repo, err
} }
@@ -1532,18 +1487,16 @@ func (s *Store) RepoNameExists(projectID string, name string, repoType string) (
func (s *Store) GetRepo(id string) (models.Repo, error) { func (s *Store) GetRepo(id string) (models.Repo, error) {
var repo models.Repo var repo models.Repo
var row *sql.Row var row *sql.Row
var created time.Time
var err error var err error
row = s.QueryRow(`SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at row = s.QueryRow(`SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at
FROM repos r FROM repos r
JOIN projects p ON p.id = r.project_id JOIN projects p ON p.id = r.project_id
JOIN users u ON u.id = r.created_by JOIN users u ON u.id = r.created_by
WHERE r.public_id = ?`, id) WHERE r.public_id = ?`, id)
err = row.Scan(&repo.ID, &repo.ProjectID, &repo.Name, &repo.Type, &repo.Path, &repo.CreatedBy, &created) err = row.Scan(&repo.ID, &repo.ProjectID, &repo.Name, &repo.Type, &repo.Path, &repo.CreatedBy, &repo.CreatedAt)
if err != nil { if err != nil {
return repo, err return repo, err
} }
repo.CreatedAt = created.Unix()
return repo, nil return repo, nil
} }
@@ -1567,7 +1520,6 @@ func (s *Store) GetRepoByProjectNameType(projectID string, name string, repoType
var repo models.Repo var repo models.Repo
var row *sql.Row var row *sql.Row
var err error var err error
var created time.Time
row = s.QueryRow(`SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at row = s.QueryRow(`SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at
FROM repos r FROM repos r
JOIN projects p ON p.id = r.project_id JOIN projects p ON p.id = r.project_id
@@ -1575,11 +1527,10 @@ func (s *Store) GetRepoByProjectNameType(projectID string, name string, repoType
WHERE r.project_id = (SELECT id FROM projects WHERE public_id = ?) WHERE r.project_id = (SELECT id FROM projects WHERE public_id = ?)
AND r.name = ? AND r.type = ?`, AND r.name = ? AND r.type = ?`,
projectID, name, repoType) projectID, name, repoType)
err = row.Scan(&repo.ID, &repo.ProjectID, &repo.Name, &repo.Type, &repo.Path, &repo.CreatedBy, &created) err = row.Scan(&repo.ID, &repo.ProjectID, &repo.Name, &repo.Type, &repo.Path, &repo.CreatedBy, &repo.CreatedAt)
if err != nil { if err != nil {
return repo, err return repo, err
} }
repo.CreatedAt = created.Unix()
return repo, nil return repo, nil
} }
@@ -1589,7 +1540,6 @@ func (s *Store) ListRepos(projectID string) ([]models.Repo, error) {
var repos []models.Repo var repos []models.Repo
var r models.Repo var r models.Repo
var isForeign int var isForeign int
var created time.Time
rows, err = s.Query(` rows, err = s.Query(`
SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at, 0 AS is_foreign SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at, 0 AS is_foreign
FROM repos r FROM repos r
@@ -1611,11 +1561,10 @@ func (s *Store) ListRepos(projectID string) ([]models.Repo, error) {
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &created, &isForeign) err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &r.CreatedAt, &isForeign)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.CreatedAt = created.Unix()
r.IsForeign = isForeign != 0 r.IsForeign = isForeign != 0
repos = append(repos, r) repos = append(repos, r)
} }
@@ -1627,7 +1576,6 @@ func (s *Store) ListReposOwned(projectID string) ([]models.Repo, error) {
var err error var err error
var repos []models.Repo var repos []models.Repo
var r models.Repo var r models.Repo
var created time.Time
rows, err = s.Query(` rows, err = s.Query(`
SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at
FROM repos r FROM repos r
@@ -1641,11 +1589,10 @@ func (s *Store) ListReposOwned(projectID string) ([]models.Repo, error) {
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &created) err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &r.CreatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.CreatedAt = created.Unix()
repos = append(repos, r) repos = append(repos, r)
} }
return repos, rows.Err() return repos, rows.Err()
@@ -1656,7 +1603,6 @@ func (s *Store) ListAllRepos() ([]models.Repo, error) {
var err error var err error
var repos []models.Repo var repos []models.Repo
var r models.Repo var r models.Repo
var created time.Time
rows, err = s.Query(` rows, err = s.Query(`
SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at SELECT r.public_id, p.public_id, r.name, r.type, r.path, u.public_id, r.created_at
FROM repos r FROM repos r
@@ -1669,11 +1615,10 @@ func (s *Store) ListAllRepos() ([]models.Repo, error) {
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &created) err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &r.CreatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.CreatedAt = created.Unix()
repos = append(repos, r) repos = append(repos, r)
} }
return repos, rows.Err() return repos, rows.Err()
@@ -1684,7 +1629,6 @@ func (s *Store) ListReposByProjectIDs(projectIDs []string) ([]models.Repo, error
var err error var err error
var repos []models.Repo var repos []models.Repo
var r models.Repo var r models.Repo
var created time.Time
var placeholders []string var placeholders []string
var args []interface{} var args []interface{}
var i int var i int
@@ -1715,11 +1659,10 @@ func (s *Store) ListReposByProjectIDs(projectIDs []string) ([]models.Repo, error
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &created) err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &r.CreatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.CreatedAt = created.Unix()
repos = append(repos, r) repos = append(repos, r)
} }
return repos, rows.Err() return repos, rows.Err()
@@ -1781,7 +1724,7 @@ func (s *Store) AttachRepoToProject(projectID string, repoID string) error {
var err error var err error
_, err = s.Exec(`INSERT INTO project_repos (project_id, repo_id, created_at) _, err = s.Exec(`INSERT INTO project_repos (project_id, repo_id, created_at)
VALUES ((SELECT id FROM projects WHERE public_id = ?), (SELECT id FROM repos WHERE public_id = ?), ?)`, VALUES ((SELECT id FROM projects WHERE public_id = ?), (SELECT id FROM repos WHERE public_id = ?), ?)`,
projectID, repoID, time.Now().UTC()) projectID, repoID, time.Now().UTC().Unix())
return err return err
} }
@@ -1825,7 +1768,6 @@ func (s *Store) ListAvailableReposForProject(projectID string, query string, lim
var err error var err error
var repos []models.Repo var repos []models.Repo
var r models.Repo var r models.Repo
var created time.Time
if limit <= 0 { if limit <= 0 {
limit = 100 limit = 100
} }
@@ -1871,11 +1813,10 @@ func (s *Store) ListAvailableReposForProject(projectID string, query string, lim
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &created) err = rows.Scan(&r.ID, &r.ProjectID, &r.Name, &r.Type, &r.Path, &r.CreatedBy, &r.CreatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.CreatedAt = created.Unix()
r.IsForeign = false r.IsForeign = false
repos = append(repos, r) repos = append(repos, r)
} }
@@ -1885,7 +1826,6 @@ func (s *Store) ListAvailableReposForProject(projectID string, query string, lim
func (s *Store) CreateIssue(issue models.Issue) (models.Issue, error) { func (s *Store) CreateIssue(issue models.Issue) (models.Issue, error) {
var id string var id string
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
if issue.ID == "" { if issue.ID == "" {
id, err = util.NewID() id, err = util.NewID()
@@ -1894,8 +1834,7 @@ func (s *Store) CreateIssue(issue models.Issue) (models.Issue, error) {
} }
issue.ID = id issue.ID = id
} }
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
issue.CreatedAt = nowUnix issue.CreatedAt = nowUnix
issue.UpdatedAt = nowUnix issue.UpdatedAt = nowUnix
if issue.Status == "" { if issue.Status == "" {
@@ -1904,27 +1843,23 @@ func (s *Store) CreateIssue(issue models.Issue) (models.Issue, error) {
_, err = s.Exec(` _, err = s.Exec(`
INSERT INTO issues (public_id, project_id, title, body, status, created_by, assignee_id, created_at, updated_at) INSERT INTO issues (public_id, project_id, title, body, status, created_by, assignee_id, created_at, updated_at)
VALUES (?, (SELECT id FROM projects WHERE public_id = ?), ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), (SELECT id FROM users WHERE public_id = ?), ?, ?) VALUES (?, (SELECT id FROM projects WHERE public_id = ?), ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), (SELECT id FROM users WHERE public_id = ?), ?, ?)
`, issue.ID, issue.ProjectID, issue.Title, issue.Body, issue.Status, issue.CreatedBy, issue.AssigneeID, now, now) `, issue.ID, issue.ProjectID, issue.Title, issue.Body, issue.Status, issue.CreatedBy, issue.AssigneeID, nowUnix, nowUnix)
return issue, err return issue, err
} }
func (s *Store) UpdateIssue(issue models.Issue) error { func (s *Store) UpdateIssue(issue models.Issue) error {
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
issue.UpdatedAt = nowUnix issue.UpdatedAt = nowUnix
_, err = s.Exec(`UPDATE issues SET title = ?, body = ?, status = ?, assignee_id = (SELECT id FROM users WHERE public_id = ?), updated_at = ? WHERE public_id = ?`, _, err = s.Exec(`UPDATE issues SET title = ?, body = ?, status = ?, assignee_id = (SELECT id FROM users WHERE public_id = ?), updated_at = ? WHERE public_id = ?`,
issue.Title, issue.Body, issue.Status, issue.AssigneeID, now, issue.ID) issue.Title, issue.Body, issue.Status, issue.AssigneeID, nowUnix, issue.ID)
return err return err
} }
func (s *Store) GetIssue(id string) (models.Issue, error) { func (s *Store) GetIssue(id string) (models.Issue, error) {
var issue models.Issue var issue models.Issue
var row *sql.Row var row *sql.Row
var created time.Time
var updated time.Time
var err error var err error
row = s.QueryRow(`SELECT i.public_id, p.public_id, i.title, i.body, i.status, cu.public_id, COALESCE(au.public_id, ''), i.created_at, i.updated_at row = s.QueryRow(`SELECT i.public_id, p.public_id, i.title, i.body, i.status, cu.public_id, COALESCE(au.public_id, ''), i.created_at, i.updated_at
FROM issues i FROM issues i
@@ -1932,12 +1867,10 @@ func (s *Store) GetIssue(id string) (models.Issue, error) {
JOIN users cu ON cu.id = i.created_by JOIN users cu ON cu.id = i.created_by
LEFT JOIN users au ON au.id = i.assignee_id LEFT JOIN users au ON au.id = i.assignee_id
WHERE i.public_id = ?`, id) WHERE i.public_id = ?`, id)
err = row.Scan(&issue.ID, &issue.ProjectID, &issue.Title, &issue.Body, &issue.Status, &issue.CreatedBy, &issue.AssigneeID, &created, &updated) err = row.Scan(&issue.ID, &issue.ProjectID, &issue.Title, &issue.Body, &issue.Status, &issue.CreatedBy, &issue.AssigneeID, &issue.CreatedAt, &issue.UpdatedAt)
if err != nil { if err != nil {
return issue, err return issue, err
} }
issue.CreatedAt = created.Unix()
issue.UpdatedAt = updated.Unix()
return issue, nil return issue, nil
} }
@@ -1946,8 +1879,6 @@ func (s *Store) ListIssues(projectID string) ([]models.Issue, error) {
var err error var err error
var issues []models.Issue var issues []models.Issue
var issue models.Issue var issue models.Issue
var created time.Time
var updated time.Time
rows, err = s.Query(`SELECT i.public_id, p.public_id, i.title, i.body, i.status, cu.public_id, COALESCE(au.public_id, ''), i.created_at, i.updated_at rows, err = s.Query(`SELECT i.public_id, p.public_id, i.title, i.body, i.status, cu.public_id, COALESCE(au.public_id, ''), i.created_at, i.updated_at
FROM issues i FROM issues i
JOIN projects p ON p.id = i.project_id JOIN projects p ON p.id = i.project_id
@@ -1960,12 +1891,10 @@ func (s *Store) ListIssues(projectID string) ([]models.Issue, error) {
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err = rows.Scan(&issue.ID, &issue.ProjectID, &issue.Title, &issue.Body, &issue.Status, &issue.CreatedBy, &issue.AssigneeID, &created, &updated) err = rows.Scan(&issue.ID, &issue.ProjectID, &issue.Title, &issue.Body, &issue.Status, &issue.CreatedBy, &issue.AssigneeID, &issue.CreatedAt, &issue.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
issue.CreatedAt = created.Unix()
issue.UpdatedAt = updated.Unix()
issues = append(issues, issue) issues = append(issues, issue)
} }
return issues, rows.Err() return issues, rows.Err()
@@ -1974,7 +1903,6 @@ func (s *Store) ListIssues(projectID string) ([]models.Issue, error) {
func (s *Store) AddIssueComment(comment models.IssueComment) (models.IssueComment, error) { func (s *Store) AddIssueComment(comment models.IssueComment) (models.IssueComment, error) {
var id string var id string
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
if comment.ID == "" { if comment.ID == "" {
id, err = util.NewID() id, err = util.NewID()
@@ -1983,19 +1911,17 @@ func (s *Store) AddIssueComment(comment models.IssueComment) (models.IssueCommen
} }
comment.ID = id comment.ID = id
} }
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
comment.CreatedAt = nowUnix comment.CreatedAt = nowUnix
_, err = s.Exec(`INSERT INTO issue_comments (public_id, issue_id, body, created_by, created_at) _, err = s.Exec(`INSERT INTO issue_comments (public_id, issue_id, body, created_by, created_at)
VALUES (?, (SELECT id FROM issues WHERE public_id = ?), ?, (SELECT id FROM users WHERE public_id = ?), ?)`, VALUES (?, (SELECT id FROM issues WHERE public_id = ?), ?, (SELECT id FROM users WHERE public_id = ?), ?)`,
comment.ID, comment.IssueID, comment.Body, comment.CreatedBy, now) comment.ID, comment.IssueID, comment.Body, comment.CreatedBy, nowUnix)
return comment, err return comment, err
} }
func (s *Store) CreateWikiPage(page models.WikiPage) (models.WikiPage, error) { func (s *Store) CreateWikiPage(page models.WikiPage) (models.WikiPage, error) {
var id string var id string
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
if page.ID == "" { if page.ID == "" {
id, err = util.NewID() id, err = util.NewID()
@@ -2004,23 +1930,20 @@ func (s *Store) CreateWikiPage(page models.WikiPage) (models.WikiPage, error) {
} }
page.ID = id page.ID = id
} }
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
page.UpdatedAt = nowUnix page.UpdatedAt = nowUnix
_, err = s.Exec(`INSERT INTO wiki_pages (public_id, project_id, title, slug, body, created_by, updated_at) _, err = s.Exec(`INSERT INTO wiki_pages (public_id, project_id, title, slug, body, created_by, updated_at)
VALUES (?, (SELECT id FROM projects WHERE public_id = ?), ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), ?)`, VALUES (?, (SELECT id FROM projects WHERE public_id = ?), ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), ?)`,
page.ID, page.ProjectID, page.Title, page.Slug, page.Body, page.CreatedBy, now) page.ID, page.ProjectID, page.Title, page.Slug, page.Body, page.CreatedBy, nowUnix)
return page, err return page, err
} }
func (s *Store) UpdateWikiPage(page models.WikiPage) error { func (s *Store) UpdateWikiPage(page models.WikiPage) error {
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
page.UpdatedAt = nowUnix page.UpdatedAt = nowUnix
_, err = s.Exec(`UPDATE wiki_pages SET title = ?, body = ?, updated_at = ? WHERE public_id = ?`, page.Title, page.Body, now, page.ID) _, err = s.Exec(`UPDATE wiki_pages SET title = ?, body = ?, updated_at = ? WHERE public_id = ?`, page.Title, page.Body, nowUnix, page.ID)
return err return err
} }
@@ -2029,7 +1952,6 @@ func (s *Store) ListWikiPages(projectID string) ([]models.WikiPage, error) {
var err error var err error
var pages []models.WikiPage var pages []models.WikiPage
var page models.WikiPage var page models.WikiPage
var updated time.Time
rows, err = s.Query(`SELECT w.public_id, p.public_id, w.title, w.slug, w.body, u.public_id, w.updated_at rows, err = s.Query(`SELECT w.public_id, p.public_id, w.title, w.slug, w.body, u.public_id, w.updated_at
FROM wiki_pages w FROM wiki_pages w
JOIN projects p ON p.id = w.project_id JOIN projects p ON p.id = w.project_id
@@ -2041,11 +1963,10 @@ func (s *Store) ListWikiPages(projectID string) ([]models.WikiPage, error) {
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err = rows.Scan(&page.ID, &page.ProjectID, &page.Title, &page.Slug, &page.Body, &page.CreatedBy, &updated) err = rows.Scan(&page.ID, &page.ProjectID, &page.Title, &page.Slug, &page.Body, &page.CreatedBy, &page.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
page.UpdatedAt = updated.Unix()
pages = append(pages, page) pages = append(pages, page)
} }
return pages, rows.Err() return pages, rows.Err()
@@ -2054,25 +1975,22 @@ func (s *Store) ListWikiPages(projectID string) ([]models.WikiPage, error) {
func (s *Store) GetWikiPage(id string) (models.WikiPage, error) { func (s *Store) GetWikiPage(id string) (models.WikiPage, error) {
var page models.WikiPage var page models.WikiPage
var row *sql.Row var row *sql.Row
var updated time.Time
var err error var err error
row = s.QueryRow(`SELECT w.public_id, p.public_id, w.title, w.slug, w.body, u.public_id, w.updated_at row = s.QueryRow(`SELECT w.public_id, p.public_id, w.title, w.slug, w.body, u.public_id, w.updated_at
FROM wiki_pages w FROM wiki_pages w
JOIN projects p ON p.id = w.project_id JOIN projects p ON p.id = w.project_id
JOIN users u ON u.id = w.created_by JOIN users u ON u.id = w.created_by
WHERE w.public_id = ?`, id) WHERE w.public_id = ?`, id)
err = row.Scan(&page.ID, &page.ProjectID, &page.Title, &page.Slug, &page.Body, &page.CreatedBy, &updated) err = row.Scan(&page.ID, &page.ProjectID, &page.Title, &page.Slug, &page.Body, &page.CreatedBy, &page.UpdatedAt)
if err != nil { if err != nil {
return page, err return page, err
} }
page.UpdatedAt = updated.Unix()
return page, nil return page, nil
} }
func (s *Store) CreateUpload(upload models.Upload) (models.Upload, error) { func (s *Store) CreateUpload(upload models.Upload) (models.Upload, error) {
var id string var id string
var err error var err error
var now time.Time
var nowUnix int64 var nowUnix int64
if upload.ID == "" { if upload.ID == "" {
id, err = util.NewID() id, err = util.NewID()
@@ -2081,30 +1999,27 @@ func (s *Store) CreateUpload(upload models.Upload) (models.Upload, error) {
} }
upload.ID = id upload.ID = id
} }
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
nowUnix = now.Unix()
upload.CreatedAt = nowUnix upload.CreatedAt = nowUnix
_, err = s.Exec(`INSERT INTO uploads (public_id, project_id, filename, content_type, size, storage_path, created_by, created_at) _, err = s.Exec(`INSERT INTO uploads (public_id, project_id, filename, content_type, size, storage_path, created_by, created_at)
VALUES (?, (SELECT id FROM projects WHERE public_id = ?), ?, ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), ?)`, VALUES (?, (SELECT id FROM projects WHERE public_id = ?), ?, ?, ?, ?, (SELECT id FROM users WHERE public_id = ?), ?)`,
upload.ID, upload.ProjectID, upload.Filename, upload.ContentType, upload.Size, upload.StoragePath, upload.CreatedBy, now) upload.ID, upload.ProjectID, upload.Filename, upload.ContentType, upload.Size, upload.StoragePath, upload.CreatedBy, nowUnix)
return upload, err return upload, err
} }
func (s *Store) GetUpload(id string) (models.Upload, error) { func (s *Store) GetUpload(id string) (models.Upload, error) {
var upload models.Upload var upload models.Upload
var row *sql.Row var row *sql.Row
var created time.Time
var err error var err error
row = s.QueryRow(`SELECT up.public_id, p.public_id, up.filename, up.content_type, up.size, up.storage_path, u.public_id, up.created_at row = s.QueryRow(`SELECT up.public_id, p.public_id, up.filename, up.content_type, up.size, up.storage_path, u.public_id, up.created_at
FROM uploads up FROM uploads up
JOIN projects p ON p.id = up.project_id JOIN projects p ON p.id = up.project_id
JOIN users u ON u.id = up.created_by JOIN users u ON u.id = up.created_by
WHERE up.public_id = ?`, id) WHERE up.public_id = ?`, id)
err = row.Scan(&upload.ID, &upload.ProjectID, &upload.Filename, &upload.ContentType, &upload.Size, &upload.StoragePath, &upload.CreatedBy, &created) err = row.Scan(&upload.ID, &upload.ProjectID, &upload.Filename, &upload.ContentType, &upload.Size, &upload.StoragePath, &upload.CreatedBy, &upload.CreatedAt)
if err != nil { if err != nil {
return upload, err return upload, err
} }
upload.CreatedAt = created.Unix()
return upload, nil return upload, nil
} }
@@ -2113,7 +2028,6 @@ func (s *Store) ListUploads(projectID string) ([]models.Upload, error) {
var err error var err error
var uploads []models.Upload var uploads []models.Upload
var upload models.Upload var upload models.Upload
var created time.Time
rows, err = s.Query(`SELECT up.public_id, p.public_id, up.filename, up.content_type, up.size, up.storage_path, u.public_id, up.created_at rows, err = s.Query(`SELECT up.public_id, p.public_id, up.filename, up.content_type, up.size, up.storage_path, u.public_id, up.created_at
FROM uploads up FROM uploads up
JOIN projects p ON p.id = up.project_id JOIN projects p ON p.id = up.project_id
@@ -2125,11 +2039,10 @@ func (s *Store) ListUploads(projectID string) ([]models.Upload, error) {
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err = rows.Scan(&upload.ID, &upload.ProjectID, &upload.Filename, &upload.ContentType, &upload.Size, &upload.StoragePath, &upload.CreatedBy, &created) err = rows.Scan(&upload.ID, &upload.ProjectID, &upload.Filename, &upload.ContentType, &upload.Size, &upload.StoragePath, &upload.CreatedBy, &upload.CreatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
upload.CreatedAt = created.Unix()
uploads = append(uploads, upload) uploads = append(uploads, upload)
} }
return uploads, rows.Err() return uploads, rows.Err()
+7 -11
View File
@@ -14,20 +14,16 @@ type UserTOTP struct {
func (s *Store) GetUserTOTP(userID string) (UserTOTP, error) { func (s *Store) GetUserTOTP(userID string) (UserTOTP, error) {
var item UserTOTP var item UserTOTP
var row *sql.Row var row *sql.Row
var created time.Time
var updated time.Time
var err error var err error
row = s.QueryRow(` row = s.QueryRow(`
SELECT COALESCE(t.secret_encrypted, ''), COALESCE(t.pending_secret_encrypted, ''), COALESCE(t.enabled, 0), t.created_at, t.updated_at SELECT COALESCE(t.secret_encrypted, ''), COALESCE(t.pending_secret_encrypted, ''), COALESCE(t.enabled, 0), t.created_at, t.updated_at
FROM user_totp t JOIN users u ON u.id = t.user_id FROM user_totp t JOIN users u ON u.id = t.user_id
WHERE u.public_id = ? WHERE u.public_id = ?
`, userID) `, userID)
err = row.Scan(&item.SecretEncrypted, &item.PendingSecretEncrypted, &item.Enabled, &created, &updated) err = row.Scan(&item.SecretEncrypted, &item.PendingSecretEncrypted, &item.Enabled, &item.CreatedAt, &item.UpdatedAt)
if err != nil { if err != nil {
return item, err return item, err
} }
item.CreatedAt = created.Unix()
item.UpdatedAt = updated.Unix()
return item, nil return item, nil
} }
@@ -52,25 +48,25 @@ func (s *Store) UserRequiresTOTP(userID string) (bool, error) {
func (s *Store) UpsertUserTOTPPending(userID string, pendingSecretEncrypted string) error { func (s *Store) UpsertUserTOTPPending(userID string, pendingSecretEncrypted string) error {
var err error var err error
var now time.Time var nowUnix int64
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
_, err = s.Exec(` _, err = s.Exec(`
INSERT INTO user_totp (user_id, secret_encrypted, pending_secret_encrypted, enabled, created_at, updated_at) INSERT INTO user_totp (user_id, secret_encrypted, pending_secret_encrypted, enabled, created_at, updated_at)
VALUES ((SELECT id FROM users WHERE public_id = ?), '', ?, 0, ?, ?) VALUES ((SELECT id FROM users WHERE public_id = ?), '', ?, 0, ?, ?)
ON CONFLICT(user_id) DO UPDATE SET pending_secret_encrypted = excluded.pending_secret_encrypted, updated_at = excluded.updated_at ON CONFLICT(user_id) DO UPDATE SET pending_secret_encrypted = excluded.pending_secret_encrypted, updated_at = excluded.updated_at
`, userID, pendingSecretEncrypted, now, now) `, userID, pendingSecretEncrypted, nowUnix, nowUnix)
return err return err
} }
func (s *Store) EnableUserTOTP(userID string, secretEncrypted string) error { func (s *Store) EnableUserTOTP(userID string, secretEncrypted string) error {
var err error var err error
var now time.Time var nowUnix int64
now = time.Now().UTC() nowUnix = time.Now().UTC().Unix()
_, err = s.Exec(` _, err = s.Exec(`
INSERT INTO user_totp (user_id, secret_encrypted, pending_secret_encrypted, enabled, created_at, updated_at) INSERT INTO user_totp (user_id, secret_encrypted, pending_secret_encrypted, enabled, created_at, updated_at)
VALUES ((SELECT id FROM users WHERE public_id = ?), ?, '', 1, ?, ?) VALUES ((SELECT id FROM users WHERE public_id = ?), ?, '', 1, ?, ?)
ON CONFLICT(user_id) DO UPDATE SET secret_encrypted = excluded.secret_encrypted, pending_secret_encrypted = '', enabled = 1, updated_at = excluded.updated_at ON CONFLICT(user_id) DO UPDATE SET secret_encrypted = excluded.secret_encrypted, pending_secret_encrypted = '', enabled = 1, updated_at = excluded.updated_at
`, userID, secretEncrypted, now, now) `, userID, secretEncrypted, nowUnix, nowUnix)
return err return err
} }
+6 -3
View File
@@ -1815,7 +1815,7 @@ func (api *API) UpdateProject(w http.ResponseWriter, r *http.Request, params map
project.HomePage = normalizeProjectHomePage(req.HomePage) project.HomePage = normalizeProjectHomePage(req.HomePage)
} }
if project.HomePage == "" { if project.HomePage == "" {
project.HomePage = "info" project.HomePage = "settings"
} }
user, _ = middleware.UserFromContext(r.Context()) user, _ = middleware.UserFromContext(r.Context())
project.UpdatedBy = user.ID project.UpdatedBy = user.ID
@@ -5448,10 +5448,13 @@ func normalizeRole(role string) string {
func normalizeProjectHomePage(value string) string { func normalizeProjectHomePage(value string) string {
var v string var v string
v = strings.ToLower(strings.TrimSpace(value)) v = strings.ToLower(strings.TrimSpace(value))
if v == "repos" || v == "issues" || v == "wiki" || v == "files" || v == "info" { if v == "info" {
return "settings"
}
if v == "repos" || v == "issues" || v == "wiki" || v == "files" || v == "settings" {
return v return v
} }
return "info" return "settings"
} }
+4
View File
@@ -1762,6 +1762,10 @@ func (api *API) CreateSSHSessionForSelf(w http.ResponseWriter, r *http.Request,
WriteJSONWithErrorReason(w, r, http.StatusBadRequest, "server_id does not match access profile") WriteJSONWithErrorReason(w, r, http.StatusBadRequest, "server_id does not match access profile")
return return
} }
if !profile.Server.Enabled {
WriteJSONWithErrorReason(w, r, http.StatusBadRequest, "ssh server is disabled")
return
}
item = models.SSHSession{ item = models.SSHSession{
ProfileID: profile.ID, ProfileID: profile.ID,
ServerID: profile.ServerID, ServerID: profile.ServerID,
+4 -1
View File
@@ -15,7 +15,7 @@ func TestUserJSONTags(t *testing.T) {
Email: "a@x", Email: "a@x",
IsAdmin: true, IsAdmin: true,
Disabled: true, Disabled: true,
AuthSource: "db", AuthProviderID: "p1",
CreatedAt: 1, CreatedAt: 1,
UpdatedAt: 2, UpdatedAt: 2,
} }
@@ -37,6 +37,9 @@ func TestUserJSONTags(t *testing.T) {
if _, ok := decoded["disabled"]; !ok { if _, ok := decoded["disabled"]; !ok {
t.Fatalf("missing disabled json key") t.Fatalf("missing disabled json key")
} }
if _, ok := decoded["auth_provider_id"]; !ok {
t.Fatalf("missing auth_provider_id json key")
}
} }
func TestAPIKeyJSONTags(t *testing.T) { func TestAPIKeyJSONTags(t *testing.T) {
+1 -1
View File
@@ -41,7 +41,7 @@ CREATE TABLE IF NOT EXISTS projects (
slug TEXT NOT NULL UNIQUE, slug TEXT NOT NULL UNIQUE,
name TEXT NOT NULL, name TEXT NOT NULL,
description TEXT NOT NULL, description TEXT NOT NULL,
home_page TEXT NOT NULL DEFAULT 'info', home_page TEXT NOT NULL DEFAULT 'settings',
created_by INTEGER NOT NULL, created_by INTEGER NOT NULL,
updated_by INTEGER NOT NULL DEFAULT 0, updated_by INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL, created_at TIMESTAMP NOT NULL,
+106
View File
@@ -0,0 +1,106 @@
BEGIN;
-- users: created_at, updated_at
ALTER TABLE users ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
ALTER TABLE users ADD COLUMN updated_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE users SET
created_at_new = CASE WHEN typeof(created_at) = 'integer' THEN created_at ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END,
updated_at_new = CASE WHEN typeof(updated_at) = 'integer' THEN updated_at ELSE COALESCE(CAST(strftime('%s', substr(updated_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE users DROP COLUMN created_at;
ALTER TABLE users DROP COLUMN updated_at;
ALTER TABLE users RENAME COLUMN created_at_new TO created_at;
ALTER TABLE users RENAME COLUMN updated_at_new TO updated_at;
-- sessions: expires_at, created_at
ALTER TABLE sessions ADD COLUMN expires_at_new INTEGER NOT NULL DEFAULT 0;
ALTER TABLE sessions ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE sessions SET
expires_at_new = CASE WHEN typeof(expires_at) = 'integer' THEN expires_at ELSE COALESCE(CAST(strftime('%s', substr(expires_at, 1, 19)) AS INTEGER), 0) END,
created_at_new = CASE WHEN typeof(created_at) = 'integer' THEN created_at ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE sessions DROP COLUMN expires_at;
ALTER TABLE sessions DROP COLUMN created_at;
ALTER TABLE sessions RENAME COLUMN expires_at_new TO expires_at;
ALTER TABLE sessions RENAME COLUMN created_at_new TO created_at;
-- user_totp: created_at, updated_at
ALTER TABLE user_totp ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
ALTER TABLE user_totp ADD COLUMN updated_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE user_totp SET
created_at_new = CASE WHEN typeof(created_at) = 'integer' THEN created_at ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END,
updated_at_new = CASE WHEN typeof(updated_at) = 'integer' THEN updated_at ELSE COALESCE(CAST(strftime('%s', substr(updated_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE user_totp DROP COLUMN created_at;
ALTER TABLE user_totp DROP COLUMN updated_at;
ALTER TABLE user_totp RENAME COLUMN created_at_new TO created_at;
ALTER TABLE user_totp RENAME COLUMN updated_at_new TO updated_at;
-- projects: special case - use created_at_unix/updated_at_unix as source of truth
ALTER TABLE projects ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
ALTER TABLE projects ADD COLUMN updated_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE projects SET
created_at_new = CASE WHEN created_at_unix != 0 THEN created_at_unix
WHEN typeof(created_at) = 'integer' THEN created_at
ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END,
updated_at_new = CASE WHEN updated_at_unix != 0 THEN updated_at_unix
WHEN typeof(updated_at) = 'integer' THEN updated_at
ELSE COALESCE(CAST(strftime('%s', substr(updated_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE projects DROP COLUMN created_at;
ALTER TABLE projects DROP COLUMN updated_at;
ALTER TABLE projects DROP COLUMN created_at_unix;
ALTER TABLE projects DROP COLUMN updated_at_unix;
ALTER TABLE projects RENAME COLUMN created_at_new TO created_at;
ALTER TABLE projects RENAME COLUMN updated_at_new TO updated_at;
-- project_members: created_at
ALTER TABLE project_members ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE project_members SET
created_at_new = CASE WHEN typeof(created_at) = 'integer' THEN created_at ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE project_members DROP COLUMN created_at;
ALTER TABLE project_members RENAME COLUMN created_at_new TO created_at;
-- repos: created_at
ALTER TABLE repos ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE repos SET
created_at_new = CASE WHEN typeof(created_at) = 'integer' THEN created_at ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE repos DROP COLUMN created_at;
ALTER TABLE repos RENAME COLUMN created_at_new TO created_at;
-- issues: created_at, updated_at
ALTER TABLE issues ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
ALTER TABLE issues ADD COLUMN updated_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE issues SET
created_at_new = CASE WHEN typeof(created_at) = 'integer' THEN created_at ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END,
updated_at_new = CASE WHEN typeof(updated_at) = 'integer' THEN updated_at ELSE COALESCE(CAST(strftime('%s', substr(updated_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE issues DROP COLUMN created_at;
ALTER TABLE issues DROP COLUMN updated_at;
ALTER TABLE issues RENAME COLUMN created_at_new TO created_at;
ALTER TABLE issues RENAME COLUMN updated_at_new TO updated_at;
-- issue_comments: created_at
ALTER TABLE issue_comments ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE issue_comments SET
created_at_new = CASE WHEN typeof(created_at) = 'integer' THEN created_at ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE issue_comments DROP COLUMN created_at;
ALTER TABLE issue_comments RENAME COLUMN created_at_new TO created_at;
-- wiki_pages: updated_at
ALTER TABLE wiki_pages ADD COLUMN updated_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE wiki_pages SET
updated_at_new = CASE WHEN typeof(updated_at) = 'integer' THEN updated_at ELSE COALESCE(CAST(strftime('%s', substr(updated_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE wiki_pages DROP COLUMN updated_at;
ALTER TABLE wiki_pages RENAME COLUMN updated_at_new TO updated_at;
-- uploads: created_at
ALTER TABLE uploads ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE uploads SET
created_at_new = CASE WHEN typeof(created_at) = 'integer' THEN created_at ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE uploads DROP COLUMN created_at;
ALTER TABLE uploads RENAME COLUMN created_at_new TO created_at;
-- project_repos: created_at
ALTER TABLE project_repos ADD COLUMN created_at_new INTEGER NOT NULL DEFAULT 0;
UPDATE project_repos SET
created_at_new = CASE WHEN typeof(created_at) = 'integer' THEN created_at ELSE COALESCE(CAST(strftime('%s', substr(created_at, 1, 19)) AS INTEGER), 0) END;
ALTER TABLE project_repos DROP COLUMN created_at;
ALTER TABLE project_repos RENAME COLUMN created_at_new TO created_at;
COMMIT;
+1 -1
View File
@@ -44,7 +44,7 @@ func createTestUser(t *testing.T, store *db.Store, username string) models.User
Email: username + "@local", Email: username + "@local",
IsAdmin: false, IsAdmin: false,
Disabled: false, Disabled: false,
AuthSource: "db", AuthProviderID: "",
} }
user, err = store.CreateUser(user, passwordHash) user, err = store.CreateUser(user, passwordHash)
if err != nil { if err != nil {
+1 -1
View File
@@ -97,7 +97,7 @@ export interface Project {
slug: string slug: string
name: string name: string
description: string description: string
home_page?: 'info' | 'repos' | 'issues' | 'wiki' | 'files' home_page?: 'settings' | 'repos' | 'issues' | 'wiki' | 'files'
created_by?: string created_by?: string
updated_by?: string updated_by?: string
created_by_name?: string created_by_name?: string
+2 -2
View File
@@ -8,7 +8,7 @@ import LoginPage from '../pages/LoginPage'
import ProjectsPage from '../pages/ProjectsPage' import ProjectsPage from '../pages/ProjectsPage'
import GlobalReposPage from '../pages/GlobalReposPage' import GlobalReposPage from '../pages/GlobalReposPage'
import ProjectEntryPage from '../pages/ProjectEntryPage' import ProjectEntryPage from '../pages/ProjectEntryPage'
import ProjectHomePage from '../pages/ProjectHomePage' import ProjectSettingsPage from '../pages/ProjectSettingsPage'
import ReposPage from '../pages/ReposPage' import ReposPage from '../pages/ReposPage'
import AdminUsersPage from '../pages/AdminUsersPage' import AdminUsersPage from '../pages/AdminUsersPage'
import AdminUserGroupsPage from '../pages/AdminUserGroupsPage' import AdminUserGroupsPage from '../pages/AdminUserGroupsPage'
@@ -87,7 +87,7 @@ export const routes: RouteObject[] = [
) )
}, },
{ path: 'projects/:projectId', element: <ProjectEntryPage /> }, { path: 'projects/:projectId', element: <ProjectEntryPage /> },
{ path: 'projects/:projectId/info', element: <ProjectHomePage /> }, { path: 'projects/:projectId/settings', element: <ProjectSettingsPage /> },
{ path: 'projects/:projectId/repos', element: <ReposPage /> }, { path: 'projects/:projectId/repos', element: <ReposPage /> },
{ path: 'projects/:projectId/repos/:repoId', element: withRouteSuspense(<RepoDetailPage />) }, { path: 'projects/:projectId/repos/:repoId', element: withRouteSuspense(<RepoDetailPage />) },
{ path: 'projects/:projectId/repos/:repoId/branches', element: withRouteSuspense(<BranchesPage />) }, { path: 'projects/:projectId/repos/:repoId/branches', element: withRouteSuspense(<BranchesPage />) },
@@ -15,7 +15,7 @@ import remarkGfm from 'remark-gfm'
import FormDialogContent from './FormDialogContent' import FormDialogContent from './FormDialogContent'
import SelectField from './SelectField' import SelectField from './SelectField'
export type ProjectHomePageValue = 'info' | 'repos' | 'issues' | 'wiki' | 'files' export type ProjectHomePageValue = 'settings' | 'repos' | 'issues' | 'wiki' | 'files'
export type ProjectDescriptionTab = 'write' | 'preview' export type ProjectDescriptionTab = 'write' | 'preview'
type ProjectFormDialogProps = { type ProjectFormDialogProps = {
@@ -76,7 +76,7 @@ export default function ProjectFormDialog(props: ProjectFormDialogProps) {
<MenuItem value="issues">Issues</MenuItem> <MenuItem value="issues">Issues</MenuItem>
<MenuItem value="wiki">Wiki</MenuItem> <MenuItem value="wiki">Wiki</MenuItem>
<MenuItem value="files">Files</MenuItem> <MenuItem value="files">Files</MenuItem>
<MenuItem value="info">Settings</MenuItem> <MenuItem value="settings">Settings</MenuItem>
</SelectField> </SelectField>
<Box sx={{ mt: 1 }}> <Box sx={{ mt: 1 }}>
<Tabs <Tabs
+1 -1
View File
@@ -76,7 +76,7 @@ export default function ProjectNavBar(props: ProjectNavBarProps) {
</Button> </Button>
<Button <Button
component={Link} component={Link}
to={`/projects/${projectId}/info`} to={`/projects/${projectId}/settings`}
startIcon={<InfoOutlinedIcon />} startIcon={<InfoOutlinedIcon />}
sx={{ justifyContent: 'flex-start', minWidth: 0, width: 'fit-content' }} sx={{ justifyContent: 'flex-start', minWidth: 0, width: 'fit-content' }}
> >
+1 -1
View File
@@ -675,7 +675,7 @@ export default function DashboardPage() {
{project.slug} · updated {fmt(project.updated_at || project.created_at || 0)} {project.slug} · updated {fmt(project.updated_at || project.created_at || 0)}
</Typography> </Typography>
</Box> </Box>
<Chip size="small" variant="outlined" label={project.home_page || 'info'} /> <Chip size="small" variant="outlined" label={project.home_page || 'settings'} />
</Box> </Box>
))} ))}
</Box> </Box>
+2 -2
View File
@@ -8,7 +8,7 @@ function targetPath(projectId: string, homePage?: string) {
if (homePage === 'issues') return `/projects/${projectId}/issues` if (homePage === 'issues') return `/projects/${projectId}/issues`
if (homePage === 'wiki') return `/projects/${projectId}/wiki` if (homePage === 'wiki') return `/projects/${projectId}/wiki`
if (homePage === 'files') return `/projects/${projectId}/files` if (homePage === 'files') return `/projects/${projectId}/files`
return `/projects/${projectId}/info` return `/projects/${projectId}/settings`
} }
export default function ProjectEntryPage() { export default function ProjectEntryPage() {
@@ -23,7 +23,7 @@ export default function ProjectEntryPage() {
navigate(targetPath(projectId, project.home_page), { replace: true }) navigate(targetPath(projectId, project.home_page), { replace: true })
}) })
.catch(() => { .catch(() => {
navigate(`/projects/${projectId}/info`, { replace: true }) navigate(`/projects/${projectId}/settings`, { replace: true })
}) })
}, [projectId, navigate]) }, [projectId, navigate])
@@ -14,17 +14,17 @@ import DeleteIcon from '@mui/icons-material/Delete'
import ConfirmDeleteDialog from '../components/ConfirmDeleteDialog' import ConfirmDeleteDialog from '../components/ConfirmDeleteDialog'
import ProjectFormDialog from '../components/ProjectFormDialog' import ProjectFormDialog from '../components/ProjectFormDialog'
import { ProjectDescriptionTab, ProjectHomePageValue } from '../components/ProjectFormDialog' import { ProjectDescriptionTab, ProjectHomePageValue } from '../components/ProjectFormDialog'
import SelectField from '../components/SelectField'
import PageAlert from '../components/PageAlert' import PageAlert from '../components/PageAlert'
import Autocomplete from '../components/Autocomplete'
export default function ProjectHomePage() { export default function ProjectSettingsPage() {
const { projectId } = useParams() const { projectId } = useParams()
const [project, setProject] = useState<Project | null>(null) const [project, setProject] = useState<Project | null>(null)
const [editOpen, setEditOpen] = useState(false) const [editOpen, setEditOpen] = useState(false)
const [editSlug, setEditSlug] = useState('') const [editSlug, setEditSlug] = useState('')
const [editName, setEditName] = useState('') const [editName, setEditName] = useState('')
const [editDescription, setEditDescription] = useState('') const [editDescription, setEditDescription] = useState('')
const [editHomePage, setEditHomePage] = useState<ProjectHomePageValue>('info') const [editHomePage, setEditHomePage] = useState<ProjectHomePageValue>('settings')
const [editDescTab, setEditDescTab] = useState<ProjectDescriptionTab>('write') const [editDescTab, setEditDescTab] = useState<ProjectDescriptionTab>('write')
const [editError, setEditError] = useState<string | null>(null) const [editError, setEditError] = useState<string | null>(null)
const [savingEdit, setSavingEdit] = useState(false) const [savingEdit, setSavingEdit] = useState(false)
@@ -103,7 +103,7 @@ export default function ProjectHomePage() {
setEditSlug(project.slug) setEditSlug(project.slug)
setEditName(project.name) setEditName(project.name)
setEditDescription(project.description || '') setEditDescription(project.description || '')
setEditHomePage(project.home_page || 'info') setEditHomePage(project.home_page || 'settings')
setEditDescTab('write') setEditDescTab('write')
setEditError(null) setEditError(null)
setEditOpen(true) setEditOpen(true)
@@ -358,23 +358,16 @@ export default function ProjectHomePage() {
actions={ actions={
canManageMembers ? ( canManageMembers ? (
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center', flexWrap: 'wrap', justifyContent: 'flex-end' }}> <Box sx={{ display: 'flex', gap: 1, alignItems: 'center', flexWrap: 'wrap', justifyContent: 'flex-end' }}>
<SelectField <Autocomplete<User, false, false, false>
select
size="small" size="small"
label="User" options={memberUsers.filter((user: User) => !members.some((member: ProjectMember) => member.user_id === user.id))}
value={newMemberUserID} value={memberUsers.find((user: User) => user.id === newMemberUserID) || null}
onChange={(event) => setNewMemberUserID(event.target.value)} onChange={(_, value) => setNewMemberUserID(value?.id || '')}
getOptionLabel={(user: User) => user.display_name ? `${user.display_name} (${user.username})` : user.username}
isOptionEqualToValue={(option: User, value: User) => option.id === value.id}
sx={{ minWidth: 260 }} sx={{ minWidth: 260 }}
> renderInput={(params) => <TextField {...params} label="User" placeholder="Select user" />}
<MenuItem value="">Select user</MenuItem> />
{memberUsers
.filter((user) => !members.some((member) => member.user_id === user.id))
.map((user) => (
<MenuItem key={user.id} value={user.id}>
{user.display_name ? `${user.display_name} (${user.username})` : user.username}
</MenuItem>
))}
</SelectField>
<ToggleButtonGroup <ToggleButtonGroup
size="small" size="small"
exclusive exclusive
@@ -450,23 +443,16 @@ export default function ProjectHomePage() {
actions={ actions={
canManageMembers ? ( canManageMembers ? (
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center', flexWrap: 'wrap', justifyContent: 'flex-end' }}> <Box sx={{ display: 'flex', gap: 1, alignItems: 'center', flexWrap: 'wrap', justifyContent: 'flex-end' }}>
<SelectField <Autocomplete<UserGroup, false, false, false>
select
size="small" size="small"
label="Group" options={allGroups.filter((group: UserGroup) => !groupRoles.some((role: ProjectGroupRole) => role.group_id === group.id))}
value={newGroupRoleGroupID} value={allGroups.find((group: UserGroup) => group.id === newGroupRoleGroupID) || null}
onChange={(event) => setNewGroupRoleGroupID(event.target.value)} onChange={(_, value) => setNewGroupRoleGroupID(value?.id || '')}
sx={{ minWidth: 220 }} getOptionLabel={(group: UserGroup) => group.name}
> isOptionEqualToValue={(option: UserGroup, value: UserGroup) => option.id === value.id}
<MenuItem value="">Select group</MenuItem> sx={{ minWidth: 260 }}
{allGroups renderInput={(params) => <TextField {...params} label="Group" placeholder="Select group" />}
.filter((group) => !groupRoles.some((role) => role.group_id === group.id)) />
.map((group) => (
<MenuItem key={group.id} value={group.id}>
{group.name}
</MenuItem>
))}
</SelectField>
<ToggleButtonGroup <ToggleButtonGroup
size="small" size="small"
exclusive exclusive
+6 -6
View File
@@ -21,7 +21,7 @@ export default function ProjectsPage() {
const [editSlug, setEditSlug] = useState('') const [editSlug, setEditSlug] = useState('')
const [editName, setEditName] = useState('') const [editName, setEditName] = useState('')
const [editDescription, setEditDescription] = useState('') const [editDescription, setEditDescription] = useState('')
const [editHomePage, setEditHomePage] = useState<ProjectHomePageValue>('info') const [editHomePage, setEditHomePage] = useState<ProjectHomePageValue>('settings')
const [editDescTab, setEditDescTab] = useState<ProjectDescriptionTab>('write') const [editDescTab, setEditDescTab] = useState<ProjectDescriptionTab>('write')
const [editError, setEditError] = useState<string | null>(null) const [editError, setEditError] = useState<string | null>(null)
const [createOpen, setCreateOpen] = useState(false) const [createOpen, setCreateOpen] = useState(false)
@@ -30,7 +30,7 @@ export default function ProjectsPage() {
const [createError, setCreateError] = useState<string | null>(null) const [createError, setCreateError] = useState<string | null>(null)
const [creating, setCreating] = useState(false) const [creating, setCreating] = useState(false)
const [createDescription, setCreateDescription] = useState('') const [createDescription, setCreateDescription] = useState('')
const [createHomePage, setCreateHomePage] = useState<ProjectHomePageValue>('info') const [createHomePage, setCreateHomePage] = useState<ProjectHomePageValue>('settings')
const [createDescTab, setCreateDescTab] = useState<ProjectDescriptionTab>('write') const [createDescTab, setCreateDescTab] = useState<ProjectDescriptionTab>('write')
const [savingEdit, setSavingEdit] = useState(false) const [savingEdit, setSavingEdit] = useState(false)
const [deleteProject, setDeleteProject] = useState<Project | null>(null) const [deleteProject, setDeleteProject] = useState<Project | null>(null)
@@ -103,7 +103,7 @@ export default function ProjectsPage() {
setCreateSlug('') setCreateSlug('')
setCreateName('') setCreateName('')
setCreateDescription('') setCreateDescription('')
setCreateHomePage('info') setCreateHomePage('settings')
setCreateDescTab('write') setCreateDescTab('write')
setCreateOpen(false) setCreateOpen(false)
} catch (err) { } catch (err) {
@@ -119,7 +119,7 @@ export default function ProjectsPage() {
setEditSlug(project.slug) setEditSlug(project.slug)
setEditName(project.name) setEditName(project.name)
setEditDescription(project.description || '') setEditDescription(project.description || '')
setEditHomePage(project.home_page || 'info') setEditHomePage(project.home_page || 'settings')
setEditDescTab('write') setEditDescTab('write')
setEditError(null) setEditError(null)
} }
@@ -145,7 +145,7 @@ export default function ProjectsPage() {
setEditSlug('') setEditSlug('')
setEditName('') setEditName('')
setEditDescription('') setEditDescription('')
setEditHomePage('info') setEditHomePage('settings')
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : 'Failed to update project' const message = err instanceof Error ? err.message : 'Failed to update project'
setEditError(message) setEditError(message)
@@ -213,7 +213,7 @@ export default function ProjectsPage() {
setCreateSlug('') setCreateSlug('')
setCreateName('') setCreateName('')
setCreateDescription('') setCreateDescription('')
setCreateHomePage('info') setCreateHomePage('settings')
setCreateDescTab('write') setCreateDescTab('write')
} }
+2 -2
View File
@@ -80,7 +80,7 @@ export default function ReposPage() {
const [projectEditSlug, setProjectEditSlug] = useState('') const [projectEditSlug, setProjectEditSlug] = useState('')
const [projectEditName, setProjectEditName] = useState('') const [projectEditName, setProjectEditName] = useState('')
const [projectEditDescription, setProjectEditDescription] = useState('') const [projectEditDescription, setProjectEditDescription] = useState('')
const [projectEditHomePage, setProjectEditHomePage] = useState<ProjectHomePageValue>('info') const [projectEditHomePage, setProjectEditHomePage] = useState<ProjectHomePageValue>('settings')
const [projectEditDescTab, setProjectEditDescTab] = useState<ProjectDescriptionTab>('write') const [projectEditDescTab, setProjectEditDescTab] = useState<ProjectDescriptionTab>('write')
const [projectEditError, setProjectEditError] = useState<string | null>(null) const [projectEditError, setProjectEditError] = useState<string | null>(null)
const [savingProjectEdit, setSavingProjectEdit] = useState(false) const [savingProjectEdit, setSavingProjectEdit] = useState(false)
@@ -352,7 +352,7 @@ export default function ReposPage() {
setProjectEditSlug(project.slug) setProjectEditSlug(project.slug)
setProjectEditName(project.name) setProjectEditName(project.name)
setProjectEditDescription(project.description || '') setProjectEditDescription(project.description || '')
setProjectEditHomePage(project.home_page || 'info') setProjectEditHomePage(project.home_page || 'settings')
setProjectEditDescTab('write') setProjectEditDescTab('write')
setProjectEditError(null) setProjectEditError(null)
setProjectEditOpen(true) setProjectEditOpen(true)