let packages = {} let accounts = [] let indexes = {username: {}, email: {}, id: {}} let savePath = path.join([program.cwd, 'data', "packages-db.json"]) let changes = {n: 0, last: 0} let validVersion(version) = do if (version.length < 1 || version.length > 16) return false for (i from 0 to version.length) do if (i > 0 && i < version.length-1) do if (version[i] not in "abcdefghijklmnopqrstuvwxyz0123456789-.") return false else if (version[i] not in "abcdefghijklmnopqrstuvwxyz0123456789") return false return true let validName(name) = do if (name.length < 3 || name.length > 32) return false for (i from 0 to name.length) do if (i > 0 && i < name.length-1) do if (name[i] not in "abcdefghijklmnopqrstuvwxyz0123456789-.") return false else if (name[i] not in "abcdefghijklmnopqrstuvwxyz0123456789") return false return true let makeChange() = changes.n += 1 let randstr(length) = do let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" let str = [] for (i from 0 to length) do str.append(chars[random.int(0, chars.length-1)]) return str.join("") let load() = do if (path.exists(savePath)) do let file = file.read(savePath) data = file.json()||{} if ("packages" not in data) do packages = data accounts = [] makeChange() else do packages = data.packages accounts = data.accounts file.close() let forceSave() = do makeChange() save() let save() = do if (changes.n == changes.last) return changes.last = changes.n let file = file.write(savePath) file.json({packages: packages, accounts: accounts}) file.close() let saveThread() = do forever do try do save() catch (e) do term.log(e) time.snooze(10) let account = {} account.addToIndexes(acc, index) = do indexes.username[acc.username] = index indexes.email[acc.email] = index indexes.id[acc.id] = index account.makeId() = do let id = "u-"+randstr(32) if (account.getById(id) != null) return account.makeId() return id account.getById(id) = do if (id in indexes.id) return accounts[indexes.id[id]] for (i from 0 to accounts.length) do if (accounts[i].id == id) do account.addToIndexes(accounts[i], i) return accounts[i] return null account.getByUsername(username) = do if (username in indexes.username) return accounts[indexes.username[username]] for (i from 0 to accounts.length) do if (accounts[i].username == username) do account.addToIndexes(accounts[i], i) return accounts[i] return null account.getByEmail(email) = do if (email in indexes.email) return accounts[indexes.email[email]] for (i from 0 to accounts.length) do if (accounts[i].email == email) do account.addToIndexes(accounts[i], i) return accounts[i] return null account.getByToken(token) = do let split = token.splitN("|", 2) if (split.length != 2) return null let id = split[0] let token = split[1] let acc = account.getById(id) if (acc == null) return null if (token not in acc.tokens) return null return acc account.deleteToken(token) = do let split = token.split("|") if (split.length != 2) return false let id = split[0] let token = split[1] let acc = account.getById(id) if (acc == null) return false if (token not in acc.tokens) return false acc.tokens = acc.tokens.filter((t) = t != token) forceSave() return true account.makeToken(acc) = do let token = randstr(100) acc.tokens.append(token) makeChange() return acc.id+"|"+token account.saltHash(password, salt) = sha256(password+salt) account.validPassword(password) = do if (password.length < 8) return "Password must be at least 8 characters" # include at least one number let hasNumber = false for (i from 0 to password.length) do if (password[i] in "0123456789") do hasNumber = true break if (not hasNumber) return "Password must include at least one number" # include at least one uppercase letter if (password.lower() == password) return "Password must include at least one uppercase letter" # include at least one lowercase letter if (password.upper() == password) return "Password must include at least one lowercase letter" return null account.validEmail(email) = do let split = email.split("@") if (split.length != 2) return false if (split[0].length == 0) return false if (" " in split[0]) return false if (split[1].length == 0) return false if (" " in split[1]) return false if (split[1].split(".").length < 2) return false return true account.validUsername(username) = do if (username.length < 3) return false if (username.length > 32) return false for (i from 0 to username.length) do if (username[i] not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_") return false return true account.create(username, email, password) = do if (not account.validUsername(username)) return {error: "Invalid username"} if (not account.validEmail(email)) return {error: "Invalid email"} if (account.getByUsername(username) != null) return {error: "Username already exists"} if (account.getByEmail(email) != null) return {error: "Email already exists"} if ((let error = account.validPassword(password)) != null) return {error: error} let id = account.makeId() let salt = randstr(100) let hash = account.saltHash(password, salt) let acc = {id: id, username: username, email:email, password: hash, salt: salt, tokens: []} accounts.append(acc) forceSave() return acc account.login(user, password) = do let acc = account.getByUsername(user) if (acc == null) do acc = account.getByEmail(user) if (acc == null) return {error: "Icorrect username or email"} let hash = account.saltHash(password, acc.salt) if (hash != acc.password) return {error: "Incorrect password"} return {token: account.makeToken(acc)} load() t = thread(saveThread) t.start()