Removed a bunch of legacy APIs, added OAuth2 support, using Burgernotes 2.0 Draft 2 style encryption.

This commit is contained in:
Tracker-Friendly 2024-07-20 10:52:18 +01:00
parent b0a81780dc
commit 3b70fe7b4b
3 changed files with 247 additions and 271 deletions

View File

@ -3,49 +3,86 @@ Use the Burgernotes API to automate tasks, build your own client, and more!
Headers should be: "Content-type: application/json; charset=UTF-8" for all POSTs Headers should be: "Content-type: application/json; charset=UTF-8" for all POSTs
## 🔑 Authentication (version 1) ## 🔑 Authentication
POST - /api/signup - provide "username" and "password". POST - /api/signup - provide "username" and "password".
POST - /api/login - provide "username", "password" and "newpass" POST - /api/login - provide "username" and "password"
To prevent the server from knowing the encryption key, the password you provide in the request must be hashed with the SHA-3 algorithm with 128 iterations (the hash is hashed again 128 times). To prevent the server from knowing the encryption key, the password you provide in the request must be hashed with Argon2ID as per the following parameters:
- salt: UTF-8 Representation of "I munch Burgers!!" (should be 16 bytes),
- parallelism: 1
- iterations: 32
- memorySize: 19264 bytes
- hashLength: 32
- outputType: hexadecimal
Password should be at least 8 characters, username must be under 20 characters and alphanumeric. Password should be at least 8 characters, username must be under 20 characters and alphanumeric.
Newpass is a more direct call to /api/changepassword that is deprecated in version 2. Set newpass to a value of "no" in order to identify as a version 1 api and not trigger the backwards compatibility layer.
If username is taken, error code 422 will return. If username is taken, error code 422 will return.
Assuming everything went correctly, the server will return a secret key. Assuming everything went correctly, the server will return a secret key.
You'll need to store two things in local storage: You'll need to store two things in local storage:
- The secret key you just got, used to fetch notes, save stuff etc. - The secret key you just got, used to fetch notes, save stuff etc.
- A SHA512 hashed password, used as encryption key - Another password, which is hashed the same way, but with a salt of "I love Burgernotes!" (again, UTF-8 representation, 16 bytes).
### Additional notes on version 2 If you are using the OAuth2 flow (totally optional, I know it's really complex) then you should also store the login password to use later, or put the OAuth2 logic straight after this.
For version two, /api/signup and /api/login require the legacyPassword API, to allow for backwards compatibility up to version 0. To do this, set the header "X-Burgernotes-Version" to the current version number without any dots (E.G 2.1.4 -> 214). ## 🌐 OAuth2 + Burgerauth
During signup, "legacyPassword" should also be provided. legacyPassword should be the SHA-3 128 iteration hash of the Argon2ID hash of the password following these settings (yes, hashing a hash): For security purposes, traditional OAuth2 is not supported. Instead, we use Burgerauth, a custom OAuth2 implementation that provides a unique-yet-consistent "authentication code" for each user. It is created in a special way as to not involve the server, meaning the security of Burgernotes is not compromised by using OAuth2, which is normally a very server-side process.
### How it works
First, perform regular client-side OAuth2 authentication using Burgerauth, as detailed in its [own documentation](https://concord.hectabit.org/HectaBit/burgerauth/src/branch/main/APIDOCS.md). Once regular OAuth2 is complete, you will be given an authentication code, which is important for the next step.
You now have one of two options:
1. If your app is based on the web, you can host a static page provided [here](https://concord.hectabit.org/HectaBit/burgerauth/src/branch/main/keyExchangeRdir.html) on any static service. Redirect to this page with the OAuth2 token stored in localStorage as BURGERAUTH-RDIR-TOKEN. The page will then communicate with a corresponding page on Burgerauth, and transmit the key securely via RSA. You may see the page redirect a couple of times as it communicates the infomation across. All you need to know is that once it is finished, it will redirect back to the page that redirected to it with the key in localStorage as DONOTSHARE-EXCHANGED-KEY.
2. If your app is not web-based, you can open up a webview to [here](https://auth.hectabit.org/keyexchangeclient). Once it is finished, it will send a postMessage with the body "finished" to the target "*" and output "finished" to the JavaScript console. The key will be in localStorage as DONOTSHARE-EXCHANGED-KEY.
3. Alternatively, you can host a local webserver and host the aforementioned page on it. It will work the same way as the first option, and once it is finished, it will detect that it was not redirected to and instead will set it as a cookie expiring in 5 minutes and then refresh the page. You should detect for the cookie and use its value, and then kill the webserver. This method is not recommended because of its complexity and overhead.
Once this is finished, you should check if there is an existing OAuth2 entry on the server like this:
POST - /api/oauth/get - provide "username" and "oauthProvider"
oauthProvider is the name of the OAuth2 provider, such as "burgerauth" or "google" (google is used as an example, they do not use the burgerauth extensions and are therefore incompatible).
It does not have to be the actual name, but it has to be unique to the provider (per user). The sub given by OpenID Connect is a good choice.
### 404 is returned
No entry has been found, and you have to log in the user as normal.
Once this is done, you should create an entry like this:
POST - /api/oauth/new - provide "secretKey", "oauthProvider" and "encryptedPassword"
To create encryptedPassword, follow these steps:
1. Generate a random 16-byte IV.
2. Create a JSON structure like this:
```json
{
"loginPass": "(the SHA-3 password hash created in the login process)",
"cryptoPass": "(the SHA-512 password hash stored in localStorage)"
}
``` ```
Parallelism should be 1 3. Convert the JSON to a string and then encrypt it using AES-256 GCM using the exchangeKey as the key and the IV created earlier as the IV.
4. Create a JSON structure like this:
Iterations should be 256 ```json
{
Memory Allocated in bytes should be 512 "iv": "(the IV)",
"content": "(the encrypted JSON)"
Length of Hash should be 32 bytes }
The output should be in the encoded format, not the hashed format
Salt should be the SHA512 of the password
``` ```
5. Finally, convert the JSON into a string, base64 encode it, and send it off as encryptedPassword.
On login, as well as the key, the server may return "legacyPasswordNeeded" = true. Do not store the exchangeKey after this point, as it is no longer needed.
If this is the case, POST /api/v2/addlegacypassword (with the aforementioned header), provide "secretKey" and "legacyPassword" (hashed the same way as signup). ### 200 is returned
An entry exists, and encryptedPassword has been returned using JSON.
encryptedPassword is the password encrypted using AES-256 GCM, and the IV is included in the JSON, in this format defined above.
Use the passwords defined in the JSON structure before the last one to log in normally.
#### Finally, you are done!
## 🔐 Encryption ## 🔐 Encryption

425
main.go
View File

@ -17,10 +17,9 @@ import (
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie" "github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
_ "github.com/mattn/go-sqlite3" "github.com/mattn/go-sqlite3"
"github.com/spf13/viper" "github.com/spf13/viper"
"golang.org/x/crypto/scrypt" "golang.org/x/crypto/scrypt"
"golang.org/x/crypto/sha3"
) )
var ( var (
@ -32,16 +31,15 @@ var (
saltChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" saltChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
) )
func genSalt(length int) (string, error) { func randomChars(length int) (string, error) {
if length <= 0 { if length <= 0 {
log.Println("[ERROR] Known in genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", "Salt length must be at least one.") return "", errors.New("salt length must be at least one")
} }
salt := make([]byte, length) salt := make([]byte, length)
randomBytes := make([]byte, length) randomBytes := make([]byte, length)
_, err := rand.Read(randomBytes) _, err := rand.Read(randomBytes)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
return "", err return "", err
} }
@ -51,24 +49,12 @@ func genSalt(length int) (string, error) {
return string(salt), nil return string(salt), nil
} }
func hashSha3(iterations int, input string) string {
key := input
for i := 0; i < iterations; i++ {
hash := sha3.New512()
hash.Write([]byte(key))
keyBytes := hash.Sum(nil)
key = hex.EncodeToString(keyBytes)
}
return key
}
func hash(password, salt string) (string, error) { func hash(password, salt string) (string, error) {
passwordBytes := []byte(password) passwordBytes := []byte(password)
saltBytes := []byte(salt) saltBytes := []byte(salt)
derivedKey, err := scrypt.Key(passwordBytes, saltBytes, 32768, 8, 1, 64) derivedKey, err := scrypt.Key(passwordBytes, saltBytes, 32768, 8, 1, 64)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
return "", err return "", err
} }
@ -182,19 +168,19 @@ func initDb() {
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = generateDB() err = generateDB()
if err != nil { if err != nil {
log.Fatalln("[FATAL] Unknown while generating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Fatalln("[FATAL] Unknown while generating database:", err)
} }
} else { } else {
log.Print("[PROMPT] Proceeding will overwrite the database. Proceed? (y/n): ") log.Print("[PROMPT] Proceeding will overwrite the database. Proceed? (y/n): ")
var answer string var answer string
_, err := fmt.Scanln(&answer) _, err := fmt.Scanln(&answer)
if err != nil { if err != nil {
log.Fatalln("[FATAL] Unknown while scanning input at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Fatalln("[FATAL] Unknown while scanning input:", err)
} }
if strings.ToLower(answer) == "y" { if strings.ToLower(answer) == "y" {
err := generateDB() err := generateDB()
if err != nil { if err != nil {
log.Fatalln("[FATAL] Unknown while generating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Fatalln("[FATAL] Unknown while generating database:", err)
return return
} }
} else if answer == ":3" { } else if answer == ":3" {
@ -210,21 +196,23 @@ func migrateDb() {
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = generateDB() err = generateDB()
if err != nil { if err != nil {
log.Fatalln("[FATAL] Unknown while generating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Fatalln("[FATAL] Unknown while generating database:", err)
} }
} else { } else {
log.Println("[PROMPT] Proceeding will render the database unusable for older versions of Burgernotes. Proceed? (y/n): ") log.Println("[PROMPT] Proceeding will render the database unusable for older versions of Burgernotes. Proceed? (y/n): ")
var answer string var answer string
_, err := fmt.Scanln(&answer) _, err := fmt.Scanln(&answer)
if err != nil { if err != nil {
log.Fatalln("[FATAL] Unknown while scanning input at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Fatalln("[FATAL] Unknown while scanning input:", err)
} }
if strings.ToLower(answer) == "y" { if strings.ToLower(answer) == "y" {
_, err := conn.Exec("ALTER TABLE users ADD COLUMN versionTwoLegacyPassword TEXT NOT NULL DEFAULT 'nil'") _, err = conn.Exec("ALTER TABLE users DROP COLUMN versionTwoLegacyPassword")
if err != nil { if err != nil {
log.Fatalln("[FATAL] Unknown while migrating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[WARN] Unknown while migrating database (1/2):", err)
log.Println("[INFO] This is likely because your database is already migrated. This is not a problem, and Burgernotes does not need this removed - it is just for cleanup")
return return
} }
_, err = conn.Exec("CREATE TABLE oauth (id INTEGER NOT NULL, oauthProvider TEXT NOT NULL, encryptedPasswd TEXT NOT NULL)")
} else if answer == ":3" { } else if answer == ":3" {
log.Println("[:3] :3") log.Println("[:3] :3")
} else { } else {
@ -248,7 +236,7 @@ func main() {
err := viper.ReadInConfig() err := viper.ReadInConfig()
if err != nil { if err != nil {
log.Fatalln("[FATAL] Error in config file at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Fatalln("[FATAL] Error in config file:", err)
} }
host = viper.GetString("config.HOST") host = viper.GetString("config.HOST")
@ -276,12 +264,12 @@ func main() {
conn, err = sql.Open("sqlite3", "database.db") conn, err = sql.Open("sqlite3", "database.db")
if err != nil { if err != nil {
log.Fatalln("[FATAL] Cannot open database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Fatalln("[FATAL] Cannot open database:", err)
} }
defer func(conn *sql.DB) { defer func(conn *sql.DB) {
err := conn.Close() err := conn.Close()
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in main() defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in main() defer:", err)
} }
}(conn) }(conn)
@ -314,12 +302,12 @@ func main() {
}) })
router.GET("/api/version", func(c *gin.Context) { router.GET("/api/version", func(c *gin.Context) {
c.String(200, "Burgernotes Version 2.0 Beta 1") c.String(200, "Burgernotes Version 2.0 Beta 2")
}) })
router.GET("/api/versionjson", func(c *gin.Context) { router.GET("/api/versionjson", func(c *gin.Context) {
c.JSON(200, gin.H{"name": "Burgernotes", "versiontxt": "Version 2.0 Beta 1", "versionsem": "2.0.0b1", "versionnum": "200"}) c.JSON(200, gin.H{"name": "Burgernotes", "versiontxt": "Version 2.0 Beta 2", "versionsem": "2.0.0b2", "versionnum": "200"})
}) })
router.POST("/api/signup", func(c *gin.Context) { router.POST("/api/signup", func(c *gin.Context) {
var data map[string]interface{} var data map[string]interface{}
@ -340,20 +328,6 @@ func main() {
return return
} }
enableAPIVersion2 := false
versionCheck := c.GetHeader("X-Burgernotes-Version")
if versionCheck != "" {
versionCheckInt, err := strconv.Atoi(versionCheck)
if err != nil {
log.Println("[ERROR] Unknown in /api/signup versionCheck at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-VERSIONCHECK"})
return
}
if versionCheckInt > 199 {
enableAPIVersion2 = true
}
}
if username == "" || password == "" || len(username) > 20 || !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(username) { if username == "" || password == "" || len(username) > 20 || !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(username) {
c.JSON(422, gin.H{"error": "Invalid username or password"}) c.JSON(422, gin.H{"error": "Invalid username or password"})
return return
@ -361,7 +335,7 @@ func main() {
_, taken, err := checkUsernameTaken(username) _, taken, err := checkUsernameTaken(username)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/signup checkUsernameTaken() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/signup checkUsernameTaken():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-USERTAKEN"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-USERTAKEN"})
return return
} }
@ -370,44 +344,22 @@ func main() {
return return
} }
salt, err := genSalt(16) salt, err := randomChars(16)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/signup genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/signup randomChars():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SALT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SALT"})
return return
} }
hashedPasswd, err := hash(password, salt) hashedPasswd, err := hash(password, salt)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/signup hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/signup hash():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-HASH"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-HASH"})
return return
} }
if !enableAPIVersion2 { _, err = conn.Exec("INSERT INTO users (username, password, created) VALUES (?, ?, ?)", username, hashedPasswd, strconv.FormatInt(time.Now().Unix(), 10))
_, err = conn.Exec("INSERT INTO users (username, password, versionTwoLegacyPassword, created) VALUES (?, ?, ?, ?)", username, hashedPasswd, "nil", strconv.FormatInt(time.Now().Unix(), 10))
} else {
legacyPassword, ok := data["legacyPassword"].(string)
if !ok {
c.JSON(400, gin.H{"error": "Invalid JSON"})
return
}
salt, err := genSalt(16)
if err != nil {
log.Println("[ERROR] Unknown in /api/signup genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SALT"})
return
}
legacyPasswordHash, err := hash(legacyPassword, salt)
if err != nil {
log.Println("[ERROR] Unknown in /api/signup hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-HASH"})
return
}
_, err = conn.Exec("INSERT INTO users (username, password, versionTwoLegacyPassword, created) VALUES (?, ?, ?, ?)", username, hashedPasswd, legacyPasswordHash, strconv.FormatInt(time.Now().Unix(), 10))
}
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/signup Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/signup Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-DBINSERT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-DBINSERT"})
return return
} }
@ -421,15 +373,15 @@ func main() {
return return
} }
token, err := genSalt(512) token, err := randomChars(512)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/signup token genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/signup token randomChars():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONSALT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONSALT"})
return return
} }
_, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent")) _, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent"))
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/signup session Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/signup session Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONINSERT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONINSERT"})
return return
} }
@ -445,28 +397,6 @@ func main() {
return return
} }
enableAPIVersion2 := false
enableAPIVersion1 := false
version1PasswordChange, _ := data["newpass"].(string)
versionCheck := c.GetHeader("X-Burgernotes-Version")
if versionCheck != "" {
versionCheckInt, err := strconv.Atoi(versionCheck)
if err != nil {
log.Println("[ERROR] Unknown in /api/login versionCheck at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-VERSIONCHECK"})
return
}
if versionCheckInt > 199 {
enableAPIVersion2 = true
}
} else {
if version1PasswordChange != "" {
enableAPIVersion1 = true
} else {
enableAPIVersion1 = false
}
}
username, ok := data["username"].(string) username, ok := data["username"].(string)
if !ok { if !ok {
c.JSON(400, gin.H{"error": "Invalid JSON"}) c.JSON(400, gin.H{"error": "Invalid JSON"})
@ -483,117 +413,52 @@ func main() {
c.JSON(401, gin.H{"error": "User does not exist"}) c.JSON(401, gin.H{"error": "User does not exist"})
return return
} else if err != nil { } else if err != nil {
log.Println("[ERROR] Unknown in /api/login checkUsernameTaken() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/login checkUsernameTaken():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-USERTAKEN"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-USERTAKEN"})
return return
} }
if enableAPIVersion2 || enableAPIVersion1 { _, _, hashedPasswd, err := getUser(userid)
_, _, hashedPasswd, err := getUser(userid)
if err != nil {
log.Println("[ERROR] Unknown in /api/login getUser() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-GETUSER"})
return
}
correctPassword, err := verifyHash(hashedPasswd, password)
if err != nil {
if errors.Is(err, errors.New("invalid hash format")) {
c.JSON(422, gin.H{"error": "Invalid hash format"})
return
} else {
log.Println("[ERROR] Unknown in /api/login verifyHash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-VERIFYHASH"})
return
}
}
if !correctPassword {
c.JSON(401, gin.H{"error": "Incorrect password"})
return
}
} else {
var legacyPassword string
err = conn.QueryRow("SELECT versionTwoLegacyPassword FROM users WHERE id = ?", userid).Scan(&legacyPassword)
if err != nil {
log.Println("[ERROR] Unknown in /api/login legacyPassword query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-LEGACYQUERY"})
return
}
hashedPassword := hashSha3(128, password)
correctPassword, err := verifyHash(hashedPassword, password)
if err != nil {
if errors.Is(err, errors.New("invalid hash format")) {
c.JSON(422, gin.H{"error": "Invalid hash format"})
return
} else {
log.Println("[ERROR] Unknown in /api/login verifyHash() legacy at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-VERIFYHASH"})
return
}
}
if !correctPassword {
c.JSON(401, gin.H{"error": "Incorrect password"})
return
}
}
if enableAPIVersion1 && version1PasswordChange != "null" {
salt, err := genSalt(16)
if err != nil {
log.Println("[ERROR] Unknown in /api/login genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-SALT"})
return
}
hashedPassword, err := hash(version1PasswordChange, salt)
if err != nil {
log.Println("[ERROR] Unknown in /api/login hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-HASH"})
return
}
_, err = conn.Exec("UPDATE users SET password = ? WHERE id = ?", hashedPassword, userid)
if err != nil {
log.Println("[ERROR] Unknown in /api/login Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-DBUPDATE"})
return
}
}
token, err := genSalt(512)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/login token genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/login getUser():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-GETUSER"})
return
}
correctPassword, err := verifyHash(hashedPasswd, password)
if err != nil {
if errors.Is(err, errors.New("invalid hash format")) {
c.JSON(422, gin.H{"error": "Invalid hash format"})
return
} else {
log.Println("[ERROR] Unknown in /api/login verifyHash():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-VERIFYHASH"})
return
}
}
if !correctPassword {
c.JSON(401, gin.H{"error": "Incorrect password"})
return
}
token, err := randomChars(512)
if err != nil {
log.Println("[ERROR] Unknown in /api/login token randomChars():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-SESSIONSALT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-SESSIONSALT"})
return return
} }
_, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent")) _, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent"))
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/login session Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/login session Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-SESSIONINSERT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-SESSIONINSERT"})
return return
} }
if enableAPIVersion2 { c.JSON(200, gin.H{"key": token})
var legacyPassword string
err = conn.QueryRow("SELECT versionTwoLegacyPassword FROM users WHERE id = ?", userid).Scan(&legacyPassword)
if err != nil {
log.Println("[ERROR] Unknown in /api/login legacyPassword query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-LEGACYQUERY"})
return
}
if legacyPassword != "nil" {
c.JSON(200, gin.H{"key": token, "legacyPasswordNeeded": true})
} else {
c.JSON(200, gin.H{"key": token, "legacyPasswordNeeded": false})
}
return
} else {
c.JSON(200, gin.H{"key": token})
}
}) })
router.POST("/api/v2/addlegacypassword", func(c *gin.Context) { router.POST("/api/oauth/get", func(c *gin.Context) {
var data map[string]interface{} var data map[string]interface{}
err := c.ShouldBindJSON(&data) err := c.ShouldBindJSON(&data)
if err != nil { if err != nil {
@ -601,20 +466,42 @@ func main() {
return return
} }
versionCheck := c.GetHeader("X-Burgernotes-Version") username, ok := data["username"].(string)
if versionCheck != "" { if !ok {
versionCheckInt, err := strconv.Atoi(versionCheck) c.JSON(400, gin.H{"error": "Invalid JSON"})
if err != nil { return
log.Println("[ERROR] Unknown in /api/login versionCheck at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) }
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-ADDLEGACYPASSWORD-VERSIONCHECK"}) oauthProvider, ok := data["oauthProvider"].(string)
return if !ok {
c.JSON(400, gin.H{"error": "Invalid JSON"})
return
}
_, userid, err := checkUsernameTaken(username)
if err != nil {
c.JSON(404, gin.H{"error": "Username not found"})
return
}
var encryptedPasswd string
err = conn.QueryRow("SELECT encryptedPasswd FROM oauth WHERE id = ? AND oauthProvider = ?", userid, oauthProvider).Scan(&encryptedPasswd)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
c.JSON(404, gin.H{"error": "Entry not found"})
} else {
log.Println("[ERROR] Unknown in /api/oauth/get select:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-OAUTH-GET-SELECT"})
} }
if versionCheckInt < 200 { }
c.JSON(400, gin.H{"error": "This API can only be accessed by Burgernotes 2.0 and above"})
return c.JSON(200, gin.H{"password": encryptedPasswd})
} })
} else {
c.JSON(400, gin.H{"error": "This API can only be accessed by Burgernotes 2.0 and above"}) router.POST("/api/oauth/add", func(c *gin.Context) {
var data map[string]interface{}
err := c.ShouldBindJSON(&data)
if err != nil {
c.JSON(400, gin.H{"error": "Invalid JSON"})
return return
} }
@ -623,24 +510,70 @@ func main() {
c.JSON(400, gin.H{"error": "Invalid JSON"}) c.JSON(400, gin.H{"error": "Invalid JSON"})
return return
} }
legacyPassword, ok := data["legacyPassword"].(string) oauthProvider, ok := data["oauthProvider"].(string)
if !ok { if !ok {
c.JSON(400, gin.H{"error": "Invalid JSON"}) c.JSON(400, gin.H{"error": "Invalid JSON"})
return return
} }
encryptedPassword, ok := data["encryptedPassword"].(string)
if !ok {
c.JSON(400, gin.H{"error": "Invalid JSON"})
}
_, userid, err := getSession(token) _, userid, err := getSession(token)
if err != nil { if err != nil {
c.JSON(401, gin.H{"error": "Invalid session"}) c.JSON(401, gin.H{"error": "Invalid session"})
return return
} }
_, err = conn.Exec("UPDATE users SET versionTwoLegacyPassword = ? WHERE id = ?", legacyPassword, userid) _, err = conn.Exec("INSERT INTO oauth (id, oauthProvider, encryptedPasswd) VALUES (?, ?, ?)", userid, oauthProvider, encryptedPassword)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/login/addlegacypassword Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) if errors.Is(err, sqlite3.ErrConstraintUnique) {
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-ADDLEGACYPASSWORD"}) c.JSON(409, gin.H{"error": "Entry already exists"})
} else {
log.Println("[ERROR] Unknown in /api/oauth/add Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-OAUTH-ADD-EXEC"})
}
}
c.JSON(200, gin.H{"success": true})
})
router.POST("/api/oauth/remove", func(c *gin.Context) {
var data map[string]interface{}
err := c.ShouldBindJSON(&data)
if err != nil {
c.JSON(400, gin.H{"error": "Invalid JSON"})
return return
} }
token, ok := data["secretKey"].(string)
if !ok {
c.JSON(400, gin.H{"error": "Invalid JSON"})
return
}
oauthProvider, ok := data["oauthProvider"].(string)
if !ok {
c.JSON(400, gin.H{"error": "Invalid JSON"})
return
}
_, userid, err := getSession(token)
if err != nil {
c.JSON(401, gin.H{"error": "Invalid session"})
return
}
_, err = conn.Exec("DELETE FROM oauth WHERE userid = ? AND oauthProvider = ?", userid, oauthProvider)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
c.JSON(404, gin.H{"error": "Entry not found"})
} else {
log.Println("[ERROR] Unknown in /api/oauth/add Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-OAUTH-REMOVE-EXEC"})
}
}
c.JSON(200, gin.H{"success": true}) c.JSON(200, gin.H{"success": true})
}) })
@ -669,22 +602,22 @@ func main() {
return return
} }
salt, err := genSalt(16) salt, err := randomChars(16)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/changepassword genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/changepassword randomChars():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-CHANGEPASSWORD-SALT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-CHANGEPASSWORD-SALT"})
return return
} }
hashedPassword, err := hash(newPassword, salt) hashedPassword, err := hash(newPassword, salt)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/changepassword hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/changepassword hash():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-CHANGEPASSWORD-HASH"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-CHANGEPASSWORD-HASH"})
return return
} }
_, err = conn.Exec("UPDATE users SET password = ? WHERE id = ?", hashedPassword, userid) _, err = conn.Exec("UPDATE users SET password = ? WHERE id = ?", hashedPassword, userid)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/changepassword Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/changepassword Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-CHANGEPASSWORD-DBUPDATE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-CHANGEPASSWORD-DBUPDATE"})
return return
} }
@ -713,21 +646,21 @@ func main() {
created, username, _, err := getUser(userid) created, username, _, err := getUser(userid)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/userinfo getUser() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/userinfo getUser():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-USERINFO-GETUSER"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-USERINFO-GETUSER"})
return return
} }
space, err := getSpace(userid) space, err := getSpace(userid)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/userinfo getSpace() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/userinfo getSpace():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-USERINFO-GETSPACE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-USERINFO-GETSPACE"})
return return
} }
notecount, err := getNoteCount(userid) notecount, err := getNoteCount(userid)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/userinfo getNoteCount() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/userinfo getNoteCount():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-USERINFO-GETNOTECOUNT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-USERINFO-GETNOTECOUNT"})
return return
} }
@ -784,7 +717,7 @@ func main() {
c.JSON(200, []map[string]interface{}{}) c.JSON(200, []map[string]interface{}{})
return return
} else { } else {
log.Println("[ERROR] Unknown in /api/listnotes query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listnotes query:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-DBQUERY"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-DBQUERY"})
return return
} }
@ -792,7 +725,7 @@ func main() {
defer func(rows *sql.Rows) { defer func(rows *sql.Rows) {
err := rows.Close() err := rows.Close()
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/listnotes row defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listnotes row defer:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-ROWCLOSE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-ROWCLOSE"})
return return
} }
@ -803,14 +736,14 @@ func main() {
var id int var id int
var title string var title string
if err := rows.Scan(&id, &title); err != nil { if err := rows.Scan(&id, &title); err != nil {
log.Println("[ERROR] Unknown in /api/listnotes row scan at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listnotes row scan:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-ROWSCAN"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-ROWSCAN"})
return return
} }
notes = append(notes, map[string]interface{}{"id": id, "title": title}) notes = append(notes, map[string]interface{}{"id": id, "title": title})
} }
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
log.Println("[ERROR] Unknown in /api/listnotes row iteration at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listnotes row iteration:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-ROWERR"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-ROWERR"})
return return
} }
@ -843,7 +776,7 @@ func main() {
c.JSON(200, []map[string]interface{}{}) c.JSON(200, []map[string]interface{}{})
return return
} else { } else {
log.Println("[ERROR] Unknown in /api/exportnotes query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/exportnotes query:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-DBQUERY"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-DBQUERY"})
return return
} }
@ -851,7 +784,7 @@ func main() {
defer func(rows *sql.Rows) { defer func(rows *sql.Rows) {
err := rows.Close() err := rows.Close()
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/exportnotes row defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/exportnotes row defer:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-ROWCLOSE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-ROWCLOSE"})
return return
} }
@ -862,14 +795,14 @@ func main() {
var id int var id int
var created, edited, title, content string var created, edited, title, content string
if err := rows.Scan(&id, &created, &edited, &title, &content); err != nil { if err := rows.Scan(&id, &created, &edited, &title, &content); err != nil {
log.Println("[ERROR] Unknown in /api/exportnotes row scan at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/exportnotes row scan:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-ROWSCAN"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-ROWSCAN"})
return return
} }
notes = append(notes, map[string]interface{}{"id": id, "created": created, "edited": edited, "title": title, "content": content}) notes = append(notes, map[string]interface{}{"id": id, "created": created, "edited": edited, "title": title, "content": content})
} }
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
log.Println("[ERROR] Unknown in /api/exportnotes row iteration at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/exportnotes row iteration:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-ROWERR"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-ROWERR"})
return return
} }
@ -913,7 +846,7 @@ func main() {
note := note.(map[string]interface{}) note := note.(map[string]interface{})
_, err := conn.Exec("INSERT INTO notes (creator, created, edited, title, content) VALUES (?, ?, ?, ?, ?)", userid, note["created"], note["edited"], note["title"], note["content"]) _, err := conn.Exec("INSERT INTO notes (creator, created, edited, title, content) VALUES (?, ?, ?, ?, ?)", userid, note["created"], note["edited"], note["title"], note["content"])
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/importnotes Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/importnotes Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-IMPORTNOTES-DBINSERT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-IMPORTNOTES-DBINSERT"})
return return
} }
@ -953,7 +886,7 @@ func main() {
} else { } else {
_, err := conn.Exec("INSERT INTO notes (title, content, creator, created, edited) VALUES (?, ?, ?, ?, ?)", noteName, "", userid, strconv.FormatInt(time.Now().Unix(), 10), strconv.FormatInt(time.Now().Unix(), 10)) _, err := conn.Exec("INSERT INTO notes (title, content, creator, created, edited) VALUES (?, ?, ?, ?, ?)", noteName, "", userid, strconv.FormatInt(time.Now().Unix(), 10), strconv.FormatInt(time.Now().Unix(), 10))
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/newnote Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/newnote Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-NEWNOTE-DBINSERT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-NEWNOTE-DBINSERT"})
return return
} else { } else {
@ -994,7 +927,7 @@ func main() {
c.JSON(422, gin.H{"error": "Note not found"}) c.JSON(422, gin.H{"error": "Note not found"})
return return
} else { } else {
log.Println("[ERROR] Unknown in /api/readnote getNote() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/readnote getNote():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-READNOTE-GETNOTE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-READNOTE-GETNOTE"})
return return
} }
@ -1030,7 +963,7 @@ func main() {
_, err = conn.Exec("DELETE FROM notes WHERE creator = ?", userid) _, err = conn.Exec("DELETE FROM notes WHERE creator = ?", userid)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/purgenotes Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/purgenotes Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-PURGENOTES-DBDELETE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-PURGENOTES-DBDELETE"})
return return
} else { } else {
@ -1080,7 +1013,7 @@ func main() {
c.JSON(422, gin.H{"error": "Note not found"}) c.JSON(422, gin.H{"error": "Note not found"})
return return
} else { } else {
log.Println("[ERROR] Unknown in /api/editnote getNote() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/editnote getNote():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EDITNOTE-GETNOTE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EDITNOTE-GETNOTE"})
return return
} }
@ -1092,7 +1025,7 @@ func main() {
} else { } else {
_, err := conn.Exec("UPDATE notes SET content = ?, title = ?, edited = ? WHERE id = ?", content, title, strconv.FormatInt(time.Now().Unix(), 10), noteId) _, err := conn.Exec("UPDATE notes SET content = ?, title = ?, edited = ? WHERE id = ?", content, title, strconv.FormatInt(time.Now().Unix(), 10), noteId)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/editnote Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/editnote Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EDITNOTE-DBUPDATE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EDITNOTE-DBUPDATE"})
return return
} else { } else {
@ -1133,7 +1066,7 @@ func main() {
c.JSON(422, gin.H{"error": "Note not found"}) c.JSON(422, gin.H{"error": "Note not found"})
return return
} else { } else {
log.Println("[ERROR] Unknown in /api/removenote getNote() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/removenote getNote():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-REMOVENOTE-GETNOTE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-REMOVENOTE-GETNOTE"})
return return
} }
@ -1145,7 +1078,7 @@ func main() {
} else { } else {
_, err := conn.Exec("DELETE FROM notes WHERE id = ?", noteId) _, err := conn.Exec("DELETE FROM notes WHERE id = ?", noteId)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/removenote Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/removenote Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-REMOVENOTE-DBDELETE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-REMOVENOTE-DBDELETE"})
return return
} else { } else {
@ -1176,21 +1109,21 @@ func main() {
_, err = conn.Exec("DELETE FROM notes WHERE creator = ?", userid) _, err = conn.Exec("DELETE FROM notes WHERE creator = ?", userid)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/deleteaccount notes Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/deleteaccount notes Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-NOTESDELETE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-NOTESDELETE"})
return return
} }
_, err = conn.Exec("DELETE FROM users WHERE id = ?", userid) _, err = conn.Exec("DELETE FROM users WHERE id = ?", userid)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/deleteaccount user Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/deleteaccount user Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-USERDELETE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-USERDELETE"})
return return
} }
_, err = conn.Exec("DELETE FROM sessions WHERE id = ?", userid) _, err = conn.Exec("DELETE FROM sessions WHERE id = ?", userid)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/deleteaccount session Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/deleteaccount session Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-SESSIONDELETE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-SESSIONDELETE"})
return return
} }
@ -1224,7 +1157,7 @@ func main() {
c.JSON(200, []map[string]interface{}{}) c.JSON(200, []map[string]interface{}{})
return return
} else { } else {
log.Println("[ERROR] Unknown in /api/sessions/list query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/sessions/list query:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-DBQUERY"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-DBQUERY"})
return return
} }
@ -1232,7 +1165,7 @@ func main() {
defer func(rows *sql.Rows) { defer func(rows *sql.Rows) {
err := rows.Close() err := rows.Close()
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/sessions/list row defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/sessions/list row defer:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWCLOSE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWCLOSE"})
return return
} }
@ -1243,7 +1176,7 @@ func main() {
var sessionid int var sessionid int
var session, device string var session, device string
if err := rows.Scan(&sessionid, &session, &device); err != nil { if err := rows.Scan(&sessionid, &session, &device); err != nil {
log.Println("[ERROR] Unknown in /api/sessions/list row scan at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/sessions/list row scan:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWSCAN"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWSCAN"})
return return
} }
@ -1254,7 +1187,7 @@ func main() {
} }
} }
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
log.Println("[ERROR] Unknown in /api/sessions/list row iteration at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/sessions/list row iteration:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWERR"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWERR"})
return return
} }
@ -1294,7 +1227,7 @@ func main() {
c.JSON(422, gin.H{"error": "Target session not found"}) c.JSON(422, gin.H{"error": "Target session not found"})
return return
} else { } else {
log.Println("[ERROR] Unknown in /api/sessions/remove getSession() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/sessions/remove getSession():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-REMOVE-GETSESSION"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-REMOVE-GETSESSION"})
return return
} }
@ -1305,7 +1238,7 @@ func main() {
} else { } else {
_, err := conn.Exec("DELETE FROM sessions WHERE sessionid = ?", sessionId) _, err := conn.Exec("DELETE FROM sessions WHERE sessionid = ?", sessionId)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/sessions/remove Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/sessions/remove Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-REMOVE-DBDELETE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-REMOVE-DBDELETE"})
return return
} else { } else {
@ -1335,7 +1268,7 @@ func main() {
c.JSON(200, []map[string]interface{}{}) c.JSON(200, []map[string]interface{}{})
return return
} else { } else {
log.Println("[ERROR] Unknown in /api/listusers query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listusers query:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-DBQUERY"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-DBQUERY"})
return return
} }
@ -1343,7 +1276,7 @@ func main() {
defer func(rows *sql.Rows) { defer func(rows *sql.Rows) {
err := rows.Close() err := rows.Close()
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/listusers row defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listusers row defer:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-ROWCLOSE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-ROWCLOSE"})
return return
} }
@ -1354,26 +1287,26 @@ func main() {
var id int var id int
var username, created string var username, created string
if err := rows.Scan(&id, &username, &created); err != nil { if err := rows.Scan(&id, &username, &created); err != nil {
log.Println("[ERROR] Unknown in /api/listusers row scan at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listusers row scan:", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-ROWSCAN"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-ROWSCAN"})
return return
} }
space, err := getSpace(id) space, err := getSpace(id)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/listusers getSpace() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listusers getSpace():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-GETSPACE"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-GETSPACE"})
return return
} }
notes, err := getNoteCount(id) notes, err := getNoteCount(id)
if err != nil { if err != nil {
log.Println("[ERROR] Unknown in /api/listusers getNoteCount() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listusers getNoteCount():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-GETNOTECOUNT"}) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-GETNOTECOUNT"})
return return
} }
users = append(users, map[string]interface{}{"id": id, "username": username, "created": created, "space": space, "notes": notes}) users = append(users, map[string]interface{}{"id": id, "username": username, "created": created, "space": space, "notes": notes})
} }
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
log.Println("[ERROR] Unknown in /api/listusers row iteration at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) log.Println("[ERROR] Unknown in /api/listusers row iteration:", err)
} }
} }
}) })

View File

@ -6,8 +6,7 @@ CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
created TEXT NOT NULL, created TEXT NOT NULL,
username TEXT NOT NULL, username TEXT NOT NULL,
password TEXT NOT NULL, password TEXT NOT NULL
versionTwoLegacyPassword TEXT NOT NULL DEFAULT 'nil',
); );
CREATE TABLE notes ( CREATE TABLE notes (
@ -25,3 +24,10 @@ CREATE TABLE sessions (
id INTEGER NOT NULL, id INTEGER NOT NULL,
device TEXT NOT NULL DEFAULT '?' device TEXT NOT NULL DEFAULT '?'
); );
CREATE TABLE oauth (
id INTEGER NOT NULL,
oauthProvider TEXT NOT NULL,
encryptedPasswd TEXT NOT NULL,
UNIQUE (id, oauthProvider)
)