diff --git a/Tesses.CrossLang.BuildEssentials/src/buildtool.tcross b/Tesses.CrossLang.BuildEssentials/src/buildtool.tcross index e898ecc..498049e 100644 --- a/Tesses.CrossLang.BuildEssentials/src/buildtool.tcross +++ b/Tesses.CrossLang.BuildEssentials/src/buildtool.tcross @@ -49,6 +49,8 @@ func Tesses.CrossLang.BuildTool(pm) BuildProject = (this,projectDirectory)=>{ var dir = FS.MakeFull(projectDirectory); var dirStr = dir.ToString(); + + each(var item : this.DirectoriesCompiled) @@ -249,9 +251,44 @@ func Tesses.CrossLang.BuildTool(pm) { //dir / outputDir; - var dict = {}; + + var exec = Env.GetRealExecutablePath("git"); + var git_hash = ""; + var git_tag = ""; + if(FS.Local.FileExists(exec)) + { + var process = new Process(); + process.FileName = exec.ToString(); + process.Arguments = ["rev-parse","HEAD"]; + process.RedirectStandardInput = true; + process.RedirectStandardOutput = true; + process.WorkingDirectory = dirStr; + if(process.Start()) + { + var memStrm = new MemoryStream(true); + process.StandardOutput.CopyTo(memStrm); + git_hash = memStrm.GetBytes().ToString().Split("\n")[0]; + + } + } + + + + + var dict = { + BuildTime = { + Git = { + Hash = git_hash, + Tag = git_tag + } + } + }; compTimeEnv = VM.CreateEnvironment(dict); + + + + switch(compTime) { case "secure_file": diff --git a/Tesses.CrossLang.Documentation/create_doc.sh b/Tesses.CrossLang.Documentation/create_doc.sh deleted file mode 100644 index 103a589..0000000 --- a/Tesses.CrossLang.Documentation/create_doc.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -mkdir docs -crosslang run > docs/Runtime.md -cd doc_projs/RuntimeObjects -crosslang build -cd ../.. -crosslang run doc_projs/RuntimeObjects/bin/*.crvm > docs/RuntimeObjects.md \ No newline at end of file diff --git a/Tesses.CrossLang.Documentation/cross.json b/Tesses.CrossLang.Documentation/cross.json deleted file mode 100644 index 222399a..0000000 --- a/Tesses.CrossLang.Documentation/cross.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": "1.0.0.0-prod", - "info": { - "maintainer": "Mike Nolan", - "type": "console", - "repo": "https://onedev.site.tesses.net/CrossLang/CrossLangExtras", - "homepage": "https://crosslang.tesseslanguage.com/", - "license": "LGPLv3" - }, - "project_dependencies": [ - "../Tesses.CrossLang.BuildEssentials" - ], - "name": "Tesses.CrossLang.Documentation" -} diff --git a/Tesses.CrossLang.Documentation/doc_projs/RuntimeObjects/cross.json b/Tesses.CrossLang.Documentation/doc_projs/RuntimeObjects/cross.json deleted file mode 100644 index 8179e27..0000000 --- a/Tesses.CrossLang.Documentation/doc_projs/RuntimeObjects/cross.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "info": { - "type": "lib" - }, - "version": "1.0.0.0-prod", - "name": "CrossLang Runtime Objects (this and the root dictionaries are implcit)" -} \ No newline at end of file diff --git a/Tesses.CrossLang.Documentation/doc_projs/RuntimeObjects/src/main.tcross b/Tesses.CrossLang.Documentation/doc_projs/RuntimeObjects/src/main.tcross deleted file mode 100644 index d9dc90b..0000000 --- a/Tesses.CrossLang.Documentation/doc_projs/RuntimeObjects/src/main.tcross +++ /dev/null @@ -1,40 +0,0 @@ -/^Get index of string or char in string, returns -1 if not found^/ -func String.IndexOf(this, textOrChr) -{ - -} -/^Get index of string or char in string, returns -1 if not found^/ -func String.LastIndexOf(this, textOrChr) -{ - -} -/^Get the enumerator ^/ -func String.GetEnumerator(this) -{ - -} -/^Get the substring of a string, if length is not provided it spans the rest of the string provided by this^/ -func String.Substring(this, offset, $length) -{ - -} -/^Remove text from a string, if length is not provided it spans the rest of the string provided by this^/ -func String.Remove(this, offset, $length) -{ - -} -/^Trim the start of string till char is not chr, if chr is not provided space will be used^/ -func String.TrimStart(this, $chr) -{ - -} -/^Trim the end of string till char is not chr, if chr is not provided space will be used^/ -func String.TrimEnd(this, $chr) -{ - -} -/^Escape a crosslang string for eval, quote determines whether the string should be quoted or not, by default not^/ -func String.Escape(this, $eval) -{ - -} \ No newline at end of file diff --git a/Tesses.CrossLang.Documentation/docs/Runtime.md b/Tesses.CrossLang.Documentation/docs/Runtime.md deleted file mode 100644 index a4846b2..0000000 --- a/Tesses.CrossLang.Documentation/docs/Runtime.md +++ /dev/null @@ -1,497 +0,0 @@ -# CrossLang Runtime Documentation -```go -/^ Start a process ^/ -func Process.Start(process_object); -``` - -```go -/^ Get whether object is a list or dynamic list ^/ -func TypeIsList(object); -``` - -```go -/^ Get whether object is a stream ^/ -func TypeIsStream(object); -``` - -```go -/^ Get whether object is a dictionary or dynamic dictionary ^/ -func TypeIsDictionary(object); -``` - -```go -/^ Get whether object is callable ^/ -func TypeIsCallable(object); -``` - -```go -/^ Get whether object is a string ^/ -func TypeIsString(object); -``` - -```go -/^ Get whether object is a number ^/ -func TypeIsNumber(object); -``` - -```go -/^ Listen (creates application loop) ^/ -func Net.Http.ListenSimpleWithLoop(server, port); -``` - -```go -/^ Create an http request ^/ -func Net.Http.MakeRequest(url, $extra); -``` - -```go -/^ Url encode path ^/ -func Net.Http.UrlPathEncode(path); -``` - -```go -/^ Url decode query param ^/ -func Net.Http.UrlDecode(param); -``` - -```go -/^ Url encode query param ^/ -func Net.Http.UrlEncode(param); -``` - -```go -/^ Get mimetype from extension ^/ -func Net.Http.MimeType(ext); -``` - -```go -/^ Url decode path ^/ -func Net.Http.UrlPathDecode(path); -``` - -```go -/^ Html encode ^/ -func Net.Http.HtmlEncode(param); -``` - -```go -/^ Create a network stream ^/ -func Net.NetworkStream(ipv6, datagram); -``` - -```go -/^ Get whether object is a double (not a long) ^/ -func TypeIsDouble(object); -``` - -```go -/^ Clear renderer with renderer draw color ^/ -func SDL2.RenderClear(renderer); -``` - -```go -/^ Init SDL2 ^/ -func SDL2.Init(); -``` - -```go -/^ Present frame (you are finished with the frame) ^/ -func SDL2.RenderPresent(renderer); -``` - -```go -/^ Set SDL2 Renderer Draw Color ^/ -func SDL2.SetRenderDrawColor(renderer, r, g, b, a); -``` - -```go -/^ Create a SDL2 Window ^/ -func SDL2.CreateWindow(title, x, y, w, h, flags); -``` - -```go -/^ Get events ^/ -func SDL2.PollEvent(); -``` - -```go -/^ Fill a rectangle using SDL ^/ -func SDL2.RenderFillRect(renderer, dictionary_with_x_y_w_h); -``` - -```go -/^ Draw a rectangle using SDL ^/ -func SDL2.RenderDrawRect(renderer, dictionary_with_x_y_w_h); -``` - -```go -/^ Create a SDL2 Renderer ^/ -func SDL2.CreateRenderer(window, ); -``` - -```go -/^ Base64 encode ^/ -func Crypto.Base64Encode(data); -``` - -```go -/^ Sha512 Algorithm ^/ -func Crypto.Sha512($is384); -``` - -```go -/^ Sha256 Algorithm ^/ -func Crypto.Sha256($is224); -``` - -```go -/^ Sha1 Algorithm (needed for WebSocket handshake/BitTorrent etc) (don't use unless you have no other choice) ^/ -func Crypto.Sha1(); -``` - -```go -/^ Base64 decode ^/ -func Crypto.Base64Decode(str); -``` - -```go -/^ Create bytearray but with random bytes in it instead of zeros (this uses mbedtls by the way) ^/ -func Crypto.RandomBytes(byteCount, personalString); -``` - -```go -/^ Hash passwords with PBKDF2 ^/ -func Crypto.PBKDF2(pass, salt, itterations, keylen, shanum); -``` - -```go -/^ Get whether object is a long (not a double) ^/ -func TypeIsLong(object); -``` - -```go -/^ Get whether object is not null or undefined ^/ -func TypeIsDefined(object); -``` - -```go -/^ Eval source code ^/ -func VM.Eval(source); -``` - -```go -/^ Load a crossvm executable ^/ -func VM.LoadExecutable(stream); -``` - -```go -/^ Create root environment ^/ -func VM.CreateEnvironment($dict); -``` - -```go -/^ Compile Source ^/ -func VM.Compile(dict); -``` - -```go -/^ Get current environment for reflection purposes ^/ -func VM.getCurrentEnvironment(); -``` - -```go -/^ Get root environment for reflection purposes ^/ -func VM.getRootEnvironment(); -``` - -```go -/^ Get root environment as a dictionary ^/ -func VM.getRootEnvironmentAsDictionary(); -``` - -```go -/^ Get whether object is susceptible to garbage collection ^/ -func TypeIsHeap(object); -``` - -```go -/^ Parse Long from String ^/ -func ParseLong(arg, $base); -``` - -```go -/^ Parse Double from String ^/ -func ParseDouble(arg); -``` - -```go -/^ Get a field in dictionary ^/ -func Dictionary.GetField(dict, key); -``` - -```go -/^ Set a field in dictionary ^/ -func Dictionary.SetField(dict, key, value); -``` - -```go -/^ Get Dictionary Item Enumerable for the each(item : Dictionary.Items(myDict)){item.Key; item.Value;} ^/ -func Dictionary.Items(dictionary); -``` - -```go -/^ Get environment variable ^/ -func Env.GetAt(key); -``` - -```go -/^ Get cache folder ^/ -func Env.getCache(); -``` - -```go -/^ Set environment variable ^/ -func Env.SetAt(key, value); -``` - -```go -/^ Get downloads folder ^/ -func Env.getDesktop(); -``` - -```go -/^ Get platform name ^/ -func Env.getPlatform(); -``` - -```go -/^ Get documents folder ^/ -func Env.getDocuments(); -``` - -```go -/^ Get downloads folder ^/ -func Env.getDownloads(); -``` - -```go -/^ Get user folder ^/ -func Env.getUser(); -``` - -```go -/^ Get pictures folder ^/ -func Env.getPictures(); -``` - -```go -/^ Get music folder ^/ -func Env.getMusic(); -``` - -```go -/^ Get videos folder ^/ -func Env.getVideos(); -``` - -```go -/^ Get state folder ^/ -func Env.getState(); -``` - -```go -/^ Get the absolute path for executable ^/ -func Env.GetRealExecutablePath(path); -``` - -```go -/^ Get config folder ^/ -func Env.getConfig(); -``` - -```go -/^ Get data folder ^/ -func Env.getData(); -``` - -```go -/^ Get whether object is a virtual filesystem ^/ -func TypeIsVFS(object); -``` - -```go -/^ Create regex object ^/ -func Regex(regex); -``` - -```go -/^ Create thread ^/ -func Thread(callback); -``` - -```go -/^ Create bytearray with optional either size (to size it) or string argument (to fill byte array) ^/ -func ByteArray($data); -``` - -```go -/^ Get type of object ^/ -func TypeOf(object); -``` - -```go -/^ Create a Path from parts ^/ -func Path.Create(relative, parts); -``` - -```go -/^ Create Absolute Root Path ^/ -func Path.Root(); -``` - -```go -/^ Create a Path from string ^/ -func Path.FromString(path); -``` - -```go -/^ Stop the program with an optional error message ^/ -func Console.Fatal($text); -``` - -```go -/^ Reads a byte from stdin ^/ -func Console.Read(); -``` - -```go -/^ Set whether terminal is sending signals for CTRL+C (true) or via read (false) ^/ -func Console.setSignals(flag); -``` - -```go -/^ Get whether terminal is sending signals for CTRL+C (true) or via read (false) ^/ -func Console.getSignals(); -``` - -```go -/^ Write text "text" to stdout ^/ -func Console.Write(text); -``` - -```go -/^ Reads line from stdin ^/ -func Console.ReadLine(); -``` - -```go -/^ Set whether terminal is buffering line by line (true) or byte by byte (false) ^/ -func Console.setCanonical(flag); -``` - -```go -/^ Get whether terminal is buffering line by line (true) or byte by byte (false) ^/ -func Console.getCanonical(); -``` - -```go -/^ Set whether terminal is echoing characters read ^/ -func Console.setEcho(flag); -``` - -```go -/^ Write text "text" to stdout with new line ^/ -func Console.WriteLine(text); -``` - -```go -/^ Get whether terminal is echoing characters read ^/ -func Console.getEcho(); -``` - -```go -/^ Create in memory filesystem ^/ -func FS.CreateMemoryFilesystem(); -``` - -```go -/^ Create filesystem ^/ -func FS.CreateFilesystem(fs); -``` - -```go -/^ Create stream ^/ -func FS.CreateStream(strm); -``` - -```go -/^ Extract a crvm archive ^/ -func FS.ExtractArchive(strm, vfs); -``` - -```go -/^ Create a memory stream ^/ -func FS.MemoryStream(writable); -``` - -```go -/^ Create a subdir filesystem ^/ -func FS.SubdirFilesystem(fs, subdir); -``` - -```go -/^ Create a mountable filesystem ^/ -func FS.MountableFilesystem(root); -``` - -```go -/^ Write all text to file ^/ -func FS.WriteAllText(fs, filename, content); -``` - -```go -/^ Read all text from file ^/ -func FS.ReadAllText(fs, filename); -``` - -```go -/^ Create a crvm archive ^/ -func FS.CreateArchive(fs, strm, name, version, info); -``` - -```go -/^ Make absolute path from relative path ^/ -func FS.MakeFull(path); -``` - -```go -/^ Escape sql text ^/ -func Sqlite.Escape(text); -``` - -```go -/^ Close sql database ^/ -func Sqlite.Close(handle); -``` - -```go -/^ Execute sql (returns dictionary of columns key=value an error message as string or undefined) ^/ -func Sqlite.Exec(handle, sql); -``` - -```go -/^ Opens the database (returns database handle or an error message as string or undefined) ^/ -func Sqlite.Open(filename); -``` - -```go -/^ Serialize Json ^/ -func Json.Encode(any, $indent); -``` - -```go -/^ Deserialize Json ^/ -func Json.Decode(Json string); -``` - - diff --git a/Tesses.CrossLang.Documentation/docs/RuntimeObjects.md b/Tesses.CrossLang.Documentation/docs/RuntimeObjects.md deleted file mode 100644 index fcc6c99..0000000 --- a/Tesses.CrossLang.Documentation/docs/RuntimeObjects.md +++ /dev/null @@ -1,42 +0,0 @@ -# CrossLang Runtime Objects (this and the root dictionaries are implcit) Documentation -```go -/^ Get index of string or char in string returns -1 if not found ^/ -func String.IndexOf(this, textOrChr); -``` - -```go -/^ Get index of string or char in string returns -1 if not found ^/ -func String.LastIndexOf(this, textOrChr); -``` - -```go -/^ Get the enumerator ^/ -func String.GetEnumerator(this, textOrChr); -``` - -```go -/^ Get the substring of a string if length is not provided it spans the rest of the string provided by this ^/ -func String.Substring(this, textOrChr); -``` - -```go -/^ Remove text from a string if length is not provided it spans the rest of the string provided by this ^/ -func String.Remove(this, textOrChr); -``` - -```go -/^ Trim the start of string till char is not chr if chr is not provided space will be used ^/ -func String.TrimStart(this, textOrChr); -``` - -```go -/^ Trim the end of string till char is not chr if chr is not provided space will be used ^/ -func String.TrimEnd(this, textOrChr); -``` - -```go -/^ Escape a crosslang string for eval quote determines whether the string should be quoted or not by default not ^/ -func String.Escape(this, textOrChr); -``` - - diff --git a/Tesses.CrossLang.Documentation/src/main.tcross b/Tesses.CrossLang.Documentation/src/main.tcross deleted file mode 100644 index da38db2..0000000 --- a/Tesses.CrossLang.Documentation/src/main.tcross +++ /dev/null @@ -1,83 +0,0 @@ -var fns = []; -func Crawl(c,name) -{ - if(TypeOf(c) == "Dictionary") - { - each(var item : Dictionary.Items(c)) - { - Crawl(item.Value,name.Count == 0 ? item.Key : $"{name}.{item.Key}"); - } - } - else if(TypeOf(c) == "ExternalMethod") - { - fns.Add({ - Name = name, - Documentation = c.Documentation, - Arguments = c.Arguments - }); - } -} -func main(args) -{ - var md = ""; - if(args.Count == 2) - { - var strm = FS.Local.OpenFile(args[1],"rb"); - - var package = VM.LoadExecutable(strm); - - strm.Close(); - - md = $"# {package.Name} Documentation\n"; - - each(var fn : package.Functions) - { - var fnName = ""; - for(var i = 0; i < fn.FunctionNameParts.Count; i++) - { - - if(i > 0) fnName += "."; - fnName += fn.FunctionNameParts[i]; - - } - fns.Add({ - Arguments=package.Chunks[i].Arguments, - Documentation=fn.Documentation, - Name=fnName - }); - } - } - else - { - md = "# CrossLang Runtime Documentation\n"; - var dict = {}; - var rEnv = VM.CreateEnvironment(dict); - rEnv.RegisterEverything(); - Crawl(dict,""); - } - - - each(var item : fns) - { - md += "```go\n"; - if(item.Documentation.Count > 0) - { - md += "/^ "; - md += item.Documentation.Replace(",",""); - md += " ^/\n"; - } - md += "func "; - md += item.Name; - md += "("; - for(var i = 0; i < item.Arguments.Count; i++) - { - if(i > 0) md += ", "; - md += item.Arguments[i]; - } - md += ");\n"; - - md += "```\n\n"; - } - - Console.WriteLine(md); -} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/cross.json b/Tesses.CrossLang.PackageServer/cross.json index 85b0592..3fbf25a 100644 --- a/Tesses.CrossLang.PackageServer/cross.json +++ b/Tesses.CrossLang.PackageServer/cross.json @@ -4,5 +4,6 @@ "type": "console" }, "version": "1.0.0.0-prod", - "name": "Tesses.CrossLang.PackageServer" + "name": "Tesses.CrossLang.PackageServer", + "compTime": "secure" } diff --git a/Tesses.CrossLang.PackageServer/src/backend/accounts.tcross b/Tesses.CrossLang.PackageServer/src/backend/accounts.tcross new file mode 100644 index 0000000..1b64c65 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/backend/accounts.tcross @@ -0,0 +1,219 @@ +func DB.ChangeMotto(userId, motto) +{ + DB.Lock(); + var dbCon = DB.Open(); + Sqlite.Exec(dbCon,$"UPDATE accounts SET motto = {Sqlite.Escape(motto)} WHERE id = {userId};"); + var account = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE id = {userId};"); + DB.Unlock(); + if(TypeOf(account) == "List" && account.Length > 0) + { + return $"/account?name={Net.Http.UrlEncode(account[0].accountName)}"; + } + return "/"; +} +func DB.LoginButton(ctx,active,$accountPage) +{ + var session = DB.GetSession(ctx); + + + if(session != null) + { + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM sessions s inner join accounts a on s.accountId = a.id WHERE key = {Sqlite.Escape(session)};"); + + Sqlite.Close(dbCon); + DB.Unlock(); + var active2 = TypeOf(accountPage) == "String" ? accountPage == exec[0].accountName : false; + if(TypeOf(exec) == "List" && exec.Length > 0) + { + return { + active=active2, + route = $"/account?name={Net.Http.UrlEncode(exec[0].accountName)}", + text = exec[0].accountName, + admin = (ParseLong(exec[0].flags) & DB.FLAG_ADMIN) != 0, + session=session + }; + } + } + return { + active, + route = "/login", + text = "Login", + admin = false, + session="" + }; +} +func DB.GetUserInfo(userId) +{ + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon, $"SELECT * FROM accounts WHERE id = {userId};"); + Sqlite.Close(dbCon); + DB.Unlock(); + + if(TypeOf(exec) == "List" && exec.Length == 1) + { + var data = exec[0]; + data.flags = ParseLong(data.flags); + return data; + } + + return null; +} +func DB.CreateUserFromAdmin(email, name, password, flags) +{ + var res = {Success=true}; + DB.Lock(); + var dbCon = DB.Open(); + exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE accountName = {Sqlite.Escape(name)};"); + if(TypeOf(exec) == "List" && exec.Length > 0) + { + res = {Success=false, Reason = "Name already exists"}; + } + else if((flags & DB.FLAG_VERIFIED) != 0) + { + var salt = Crypto.RandomBytes(32, "CPKG"); + var hash = Crypto.PBKDF2(password, salt, DB.ITTR,64,384); + + var r = Sqlite.Exec(dbCon,$"INSERT INTO accounts (email, accountName, password_hash, password_salt, verifyExpire, flags) values ({Sqlite.Escape(email)},{Sqlite.Escape(name)},{Sqlite.Escape(Crypto.Base64Encode(hash))},{Sqlite.Escape(Crypto.Base64Encode(salt))},{0},{flags});"); + if(TypeOf(r) == "String") res = {Success = false, Reason = r}; + res = {Success=true}; + } + else { + + var salt = Crypto.RandomBytes(32, "CPKG"); + var hash = Crypto.PBKDF2(password, salt, DB.ITTR,64,384); + + var verify_hash = Crypto.RandomBytes(32, "CPKG"); + var verify_hash_str = Crypto.Base64Encode(verify_hash); + + var r = Sqlite.Exec(dbCon,$"INSERT INTO accounts (email, accountName, password_hash, password_salt, verifyKey, verifyExpire, flags) values ({Sqlite.Escape(email)},{Sqlite.Escape(name)},{Sqlite.Escape(Crypto.Base64Encode(hash))},{Sqlite.Escape(Crypto.Base64Encode(salt))},{Sqlite.Escape(verify_hash_str)},{DateTime.NowEpoch+600},{flags});"); + if(TypeOf(r) == "String") res = {Success = false, Reason = r}; + + if(DB.Config.MailConfig) + DB.SendVerifyEmail(email,name, verify_hash_str); + + res = {Success=true}; + } + Sqlite.Close(dbCon); + DB.Unlock(); + return res; +} +func DB.CreateUser(email, name, password) +{ + var res = {Success = true, Redirect="/"}; + DB.Lock(); + var dbCon = DB.Open(); + if(Sqlite.Exec(dbCon, "SELECT * FROM accounts LIMIT 1;").Length == 0) + { + //create the admin + + var salt = Crypto.RandomBytes(32, "CPKG"); + var hash = Crypto.PBKDF2(password, salt, DB.ITTR,64,384); + + var r = Sqlite.Exec(dbCon,$"INSERT INTO accounts (email, accountName, password_hash, password_salt, flags) values ({Sqlite.Escape(email)},{Sqlite.Escape(name)},{Sqlite.Escape(Crypto.Base64Encode(hash))},{Sqlite.Escape(Crypto.Base64Encode(salt))},{DB.FLAG_ADMIN|DB.FLAG_VERIFIED});"); + if(TypeOf(r) == "String") res = {Success = false, Reason = r}; + + } + else + { + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE email = {Sqlite.Escape(email)};"); + if(TypeOf(exec) == "List" && exec.Length > 0) + { + + exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE accountName = {Sqlite.Escape(name)};"); + if(TypeOf(exec) == "List" && exec.Length > 0) + { + res = {Success=false, Reason = "Email and Name already exists"}; + } + else + { + res = {Success=false, Reason = "Email already exists"}; + } + } + else { + exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE accountName = {Sqlite.Escape(name)};"); + if(TypeOf(exec) == "List" && exec.Length > 0) + { + res = {Success=false, Reason = "Name already exists"}; + } + else if(DB.Config.AllowRegister) + { + //email and name already exists + var salt = Crypto.RandomBytes(32, "CPKG"); + var hash = Crypto.PBKDF2(password, salt, DB.ITTR,64,384); + + var verify_hash = Crypto.RandomBytes(32, "CPKG"); + var verify_hash_str = Crypto.Base64Encode(verify_hash); + + var r = Sqlite.Exec(dbCon,$"INSERT INTO accounts (email, accountName, password_hash, password_salt, verifyKey, verifyExpire, flags) values ({Sqlite.Escape(email)},{Sqlite.Escape(name)},{Sqlite.Escape(Crypto.Base64Encode(hash))},{Sqlite.Escape(Crypto.Base64Encode(salt))},{Sqlite.Escape(verify_hash_str)},{DateTime.NowEpoch+600},{DB.FLAG_VERIFY});"); + if(TypeOf(r) == "String") {res = {Success = false, Reason = r};} + else { + if(DB.Config.MailConfig) + DB.SendVerifyEmail(email,name, verify_hash_str); + + res = {Success=true, Redirect="/check_email"}; + } + } + else + { + res = {Success=false, Reason="Registration is disabled on this server"}; + } + } + } + Sqlite.Close(dbCon); + DB.Unlock(); + return res; +} +func DB.GetAccountId(email, password) +{ + DB.Lock(); + + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE email = {Sqlite.Escape(email)};"); + + Sqlite.Close(dbCon); + DB.Unlock(); + var correct=false; + + if(TypeOf(exec) == "List" && exec.Length > 0) + { + var salt = Crypto.Base64Decode(exec[0].password_salt); + var hash = Crypto.PBKDF2(password, salt, DB.ITTR,64,384); + var hashStr = Crypto.Base64Encode(hash); + + if(exec[0].password_hash == hashStr) + { + return ParseLong(exec[0].id); + } + } + + return -1; +} +func DB.GetAccountInfo(name) +{ + DB.Lock(); + + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE accountName = {Sqlite.Escape(name)};"); + + Sqlite.Close(dbCon); + DB.Unlock(); + + if(TypeOf(exec) == "List") + { + if(exec.Length == 1) + { + return exec[0]; + } + else + { + return "No such user exists"; + } + } + else + { + return exec; + } +} diff --git a/Tesses.CrossLang.PackageServer/src/backend/db.tcross b/Tesses.CrossLang.PackageServer/src/backend/db.tcross index 07ae3e9..8cd96e4 100644 --- a/Tesses.CrossLang.PackageServer/src/backend/db.tcross +++ b/Tesses.CrossLang.PackageServer/src/backend/db.tcross @@ -3,347 +3,7 @@ func DB.Open() var path = DB.working / "data.db"; return Sqlite.Open(path); } -func DB.ChangeMotto(userId, motto) -{ - DB.Lock(); - var dbCon = DB.Open(); - Sqlite.Exec(dbCon,$"UPDATE accounts SET motto = {Sqlite.Escape(motto)} WHERE id = {userId};"); - var account = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE id = {userId};"); - DB.Unlock(); - if(TypeOf(account) == "List" && account.Length > 0) - { - return $"/account?name={Net.Http.UrlEncode(account[0].accountName)}"; - } - return "/"; -} -func DB.LoginButton(ctx,active,$accountPage) -{ - var session = DB.GetSession(ctx); - - if(session != null) - { - DB.Lock(); - var dbCon = DB.Open(); - var exec = Sqlite.Exec(dbCon,$"SELECT * FROM sessions s inner join accounts a on s.accountId = a.id WHERE key = {Sqlite.Escape(session)};"); - - Sqlite.Close(dbCon); - DB.Unlock(); - var active2 = TypeOf(accountPage) == "String" ? accountPage == exec[0].accountName : false; - if(TypeOf(exec) == "List" && exec.Length > 0) - { - return { - active=active2, - route = $"/account?name={Net.Http.UrlEncode(exec[0].accountName)}", - text = exec[0].accountName - }; - } - } - return { - active, - route = "/login", - text = "Login" - }; -} - -func DB.CanUploadPackagePrefix(userId, packageName) -{ - var prefix = packageName.Split(".",true,2); - if(prefix.Length >= 1) - prefix = prefix[0]; - else return false; - DB.Lock(); - var dbCon = DB.Open(); - var exec = Sqlite.Exec(dbCon, $"SELECT * FROM reserved_prefixes WHERE prefix = {Sqlite.Escape(prefix)};"); - - Sqlite.Close(dbCon); - DB.Unlock(); - - if(TypeOf(exec) == "List" && exec.Length > 0) return ParseLong(exec[0].accountId) == userId; - - return true; -} - -func DB.GetUserInfo(userId) -{ - DB.Lock(); - var dbCon = DB.Open(); - var exec = Sqlite.Exec(dbCon, $"SELECT * FROM sessions WHERE id = {userId};"); - Sqlite.Close(dbCon); - DB.Unlock(); - - if(TypeOf(exec) == "List" && exec.Length == 1) - { - var data = exec[0]; - data.flags = ParseLong(data.flags); - return data; - } - - return null; -} - -func DB.PackageExists(userId,pkgInfo) -{ - var statusCode = 4; - DB.Lock(); - var dbCon = DB.Open(); - var exec = Sqlite.Exec(dbCon,$"SELECT * from packages WHERE packageName = {Sqlite.Escape(pkgInfo.Name)};"); - if(TypeOf(exec) == "List") - { - if(exec.Length == 1) - { - var pkgId = exec[0].id; - if(ParseLong(exec[0].accountId) != userId) - statusCode=4; - else - { - exec = Sqlite.Exec(dbCon,$"SELECT * from versions WHERE packageId = {pkgId} AND version = {pkgInfo.Version.VersionInt};"); - if(TypeOf(exec) == "List") - { - if(exec.Length == 1) - { - if(pkgInfo.Version.Stage == "dev") - statusCode=3; - else - statusCode=2; - } - else statusCode=1; - } - } - } - else statusCode=0; - } - - Sqlite.Close(dbCon); - DB.Unlock(); - return statusCode; -} -func DB.AddPackage(userId,pkgInfo) -{ - DB.Lock(); - var dbCon = DB.Open(); - Sqlite.Exec(dbCon,$"INSERT INTO packages (packageName, accountId) VALUES ({Sqlite.Escape(pkgInfo.Name)}, {userId});"); - Sqlite.Close(dbCon); - DB.Unlock(); - -} -func DB.UpdateVersion(pkgInfo) -{ - - DB.Lock(); - var dbCon = DB.Open(); - var exec = Sqlite.Exec(dbCon,$"SELECT * FROM packages WHERE packageName = {Sqlite.Escape(pkgInfo.Name)};"); - var pkgId = 0; - - if(TypeOf(exec) == "List" && exec.Length == 1) - { - pkgId = exec[0].id; - } - var version = pkgInfo.Version.VersionInt; - var info = Json.Decode(pkgInfo.Info); - var description = info.description; - var type = info.type; - var maintainer = info.maintainer; - var homepage = info.homepage; - var repo = info.repo; - var license = info.license; - if(TypeOf(description) != "String") description=""; - - if(TypeOf(type) != "String") type=""; - - if(TypeOf(maintainer) != "String") maintainer=""; - - if(TypeOf(homepage) != "String") homepage=""; - - if(TypeOf(repo) != "String") repo=""; - - if(TypeOf(license) != "String") license=""; - - //CREATE TABLE IF NOT EXISTS versions (id INTEGER PRIMARY KEY AUTOINCREMENT, packageId INTEGER, version INTEGER, description TEXT, type TEXT, maintainer TEXT, homepage TEXT, repo TEXT, license TEXT); - //VALUES ({pkgId},{version},{Sqlite.Escape(description)},{Sqlite.Escape(type)},{Sqlite.Escape(maintainer)},{Sqlite.Escape(homepage)},{Sqlite.Escape(repo)},{Sqlite.Escape(license)}); - Sqlite.Exec(dbCon,$"UPDATE versions SET description = {Sqlite.Escape(description)}, type = {Sqlite.Escape(type)}, maintainer = {Sqlite.Escape(maintainer)}, homepage = {Sqlite.Escape(homepage)}, repo = {Sqlite.Escape(repo)}, license = {Sqlite.Escape(license)}, uploadTime = {DateTime.NowEpoch} WHERE packageId = {pkgId} AND version = {version};"); - Sqlite.Close(dbCon); - DB.Unlock(); - -} -func DB.AddVersion(pkgInfo) -{ - DB.Lock(); - var dbCon = DB.Open(); - var exec = Sqlite.Exec(dbCon,$"SELECT * FROM packages WHERE packageName = {Sqlite.Escape(pkgInfo.Name)};"); - var pkgId = 0; - - if(TypeOf(exec) == "List" && exec.Length == 1) - { - pkgId = exec[0].id; - } - var version = pkgInfo.Version.VersionInt; - var info = Json.Decode(pkgInfo.Info); - var description = info.description; - var type = info.type; - var maintainer = info.maintainer; - var homepage = info.homepage; - var repo = info.repo; - var license = info.license; - if(TypeOf(description) != "String") description=""; - - if(TypeOf(type) != "String") type=""; - - if(TypeOf(maintainer) != "String") maintainer=""; - - if(TypeOf(homepage) != "String") homepage=""; - - if(TypeOf(repo) != "String") repo=""; - - if(TypeOf(license) != "String") license=""; - - //CREATE TABLE IF NOT EXISTS versions (id INTEGER PRIMARY KEY AUTOINCREMENT, packageId INTEGER, version INTEGER, description TEXT, type TEXT, maintainer TEXT, homepage TEXT, repo TEXT, license TEXT); - - Sqlite.Exec(dbCon,$"INSERT INTO versions (packageId,version,description,type,maintainer,homepage,repo,license,uploadTime) VALUES ({pkgId},{version},{Sqlite.Escape(description)},{Sqlite.Escape(type)},{Sqlite.Escape(maintainer)},{Sqlite.Escape(homepage)},{Sqlite.Escape(repo)},{Sqlite.Escape(license)},{DateTime.NowEpoch});"); - Sqlite.Close(dbCon); - DB.Unlock(); - -} -func DB.GetPackageIcon(name, version) -{ - if(TypeOf(name) != "String") name = ""; - if(TypeOf(version) != "String") version = ""; - - var file = DB.working / "Packages" / name / $"{name}-{version}.crvm"; - - - if(FS.Local.FileExists(file) && name.Length > 0 && version.Length > 0) - { - var strm = FS.Local.OpenFile(file,"rb"); - - if(strm != null) - { - var exec = VM.LoadExecutable(strm); - strm.Close(); - if(exec != null) - { - if(exec.Icon != null) - { - return exec.Icon; - } - } - } - } - return embed("crosslang.png"); -} -func DB.UploadPackage(userId, filePath) -{ - if(!FS.Local.FileExists(filePath)) return {Success=false, Reason = "File does not exist"}; - var userInfo = DB.GetUserInfo(userId); - if(userInfo == null) return { Success=false, Reason = "User does not exist"}; - if((userInfo.flags & DB.FLAG_VERIFIED) == 0) return { Success=false, Reason = "User is not verified"}; - - var strm = FS.Local.OpenFile(filePath,"rb"); - var failed=false; - var reason = ""; - try { - var pkgInfo = VM.LoadExecutable(strm); - }catch(ex) { - if(ex.Type == "NativeException") - reason = ex.Text; - - failed=true; - } - - strm.Close(); - if(failed) return {Success=false, Reason = reason}; - if(!DB.CanUploadPackagePrefix(userId,pkgInfo.Name)) return { Success = false, Reason = "You can't upload a package with that prefix."}; - - switch(DB.PackageExists(userId, pkgInfo)) - { - case 0: - //package name does not exist - DB.AddPackage(userId,pkgInfo); - case 1: - DB.AddVersion(pkgInfo); - FS.Local.CreateDirectory(DB.working / "Packages" / pkgInfo.Name); - - FS.Local.MoveFile(filePath, DB.working / "Packages" / pkgInfo.Name / $"{pkgInfo.Name}-{pkgInfo.Version}.crvm"); - - - //package version does not exist - break; - case 2: - return { Success = false, Reason = "Package already exists and is not a dev package." }; - case 3: - //update package version (it exists but is dev) - - DB.UpdateVersion(pkgInfo); - FS.Local.MoveFile(filePath, DB.working / "Packages" / pkgInfo.Name / $"{pkgInfo.Name}-{pkgInfo.Version}.crvm"); - break; - case 4: - //package is not yours - return { Success = false, Reason = "You don't own the package."}; - } - - return {Success=true}; -} - -func DB.GetUserIdFromSession(session) -{ - DB.Lock(); - var dbCon = DB.Open(); - var exec = Sqlite.Exec(dbCon,$"SELECT * FROM sessions WHERE key = {Sqlite.Escape(session)};"); - - Sqlite.Close(dbCon); - DB.Unlock(); - - if(TypeOf(exec) == "List" && exec.Length == 1) return ParseLong(exec[0].accountId); - - return -1; -} -func DB.GetSessionFromBearer(ctx) -{ - var auth = ctx.RequestHeaders.TryGetFirst("Authorization"); - if(TypeOf(auth) == "String") - { - auth=auth.Split(" ",true,2); - if(auth.Length < 2) return null; - if(auth[0] != "Bearer") return null; - var uid = DB.GetUserIdFromSession(auth[1]); - if(uid != -1) return auth[1]; - } - return null; -} -func DB.GetSession(ctx) -{ - var cookie = ctx.RequestHeaders.TryGetFirst("Cookie"); - if(TypeOf(cookie) == "String") - { - each(var part : cookie.Split("; ")) - { - if(part.Length > 0) - { - var cookieKV = part.Split("=",true,2); - if(cookieKV.Length == 2 && cookieKV[0] == "Session") - { - var session = cookieKV[1]; - var sessionId = DB.GetUserIdFromSession(session); - - if(sessionId != -1) - return session; - } - } - } - } - return null; -} -func DB.CreateSession(userId) -{ - DB.Lock(); - var dbCon = DB.Open(); - - var rand = Net.Http.UrlEncode(Crypto.Base64Encode(Crypto.RandomBytes(32, "CPKG"))); - Sqlite.Exec(dbCon, $"INSERT INTO sessions (accountId,key) VALUES ({userId},{Sqlite.Escape(rand)});"); - Sqlite.Close(dbCon); - DB.Unlock(); - return rand; -} func DB.CreateCSRF(ctx) { var session = DB.GetSession(ctx); @@ -391,48 +51,7 @@ func DB.VerifyCSRF(session,csrf) DB.Unlock(); return retVal; } -func DB.GetLatestVersion(name) -{ - var sql = $"SELECT * FROM packages p join versions v on p.id = v.packageId and v.version = (SELECT MAX(version) FROM versions WHERE packageId = v.packageId) and p.packageName = {Sqlite.Escape(name)};"; - DB.Lock(); - var dbCon = DB.Open(); - var res = Sqlite.Exec(dbCon, sql); - DB.Unlock(); - if(TypeOf(res) == "List" && res.Length > 0) - { - return Version.FromLong(ParseLong(res[0].version)).ToString(); - } - return null; -} -func DB.GetPackages($page) -{ - if(TypeOf(page) != "Long") page = 1; - - var sql = "SELECT * FROM packages p join versions v on p.id = v.packageId and v.version = (SELECT MAX(version) FROM versions WHERE packageId = v.packageId);"; - DB.Lock(); - var dbCon = DB.Open(); - var res = Sqlite.Exec(dbCon, sql); - - - Sqlite.Close(dbCon); - DB.Unlock(); - var res2 = []; - each(var item : res) - { - res2.Add({ - id = item.packageId, - name = item.packageName, - version = Version.FromLong(ParseLong(item.version)).ToString(), - homepage = item.homepage, - repo = item.repo, - type = item.type, - maintainer = item.maintainer, - description = item.description - }); - } - return res2; -} func DB.Lock() { @@ -480,6 +99,8 @@ func DB.Init(working) DB.Config = { AllowRegister=false, }; + + if(TypeOf(DB.Config) != "Dictionary") DB.Config = {AllowRegister=false}; var dbCon = DB.Open(); Sqlite.Exec(dbCon,"CREATE TABLE IF NOT EXISTS packages (id INTEGER PRIMARY KEY AUTOINCREMENT, packageName TEXT UNIQUE, accountId INTEGER);"); @@ -498,204 +119,5 @@ DB.FLAG_VERIFY = 0b00000100; DB.ITTR = 35000; -func DB.GetAccountInfo(name) -{ - DB.Lock(); - var dbCon = DB.Open(); - var exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE accountName = {Sqlite.Escape(name)};"); - - Sqlite.Close(dbCon); - DB.Unlock(); - if(TypeOf(exec) == "List") - { - if(exec.Length == 1) - { - return exec[0]; - } - else - { - return "No such user exists"; - } - } - else - { - return exec; - } -} - -func DB.GetAccountId(email, password) -{ - DB.Lock(); - - var dbCon = DB.Open(); - var exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE email = {Sqlite.Escape(email)};"); - - Sqlite.Close(dbCon); - DB.Unlock(); - var correct=false; - - if(TypeOf(exec) == "List" && exec.Length > 0) - { - var salt = Crypto.Base64Decode(exec[0].password_salt); - var hash = Crypto.PBKDF2(password, salt, DB.ITTR,64,384); - var hashStr = Crypto.Base64Encode(hash); - - if(exec[0].password_hash == hashStr) - { - return ParseLong(exec[0].id); - } - } - - return -1; -} - -func DB.SendVerifyEmail(email,name, verify_hash_str) -{ - var emailText = $" -

Hello {Net.Http.HtmlEncode(name)}

- To verify your account go to {DB.Config.Prefix.TrimEnd('/')}/verify?code={Net.Http.UrlEncode(verify_hash_str)} - "; - var data = { - server = DB.Config.MailConfig.Server, - auth = DB.Config.MailConfig.Auth, - domain = DB.Config.MailConfig.Domain, - from = DB.Config.MailConfig.From, - to = email.Replace(">","").Replace("\n",""), - subject = "Verify your account on CPKG", - body = { - type = "text/html", - data = emailText - } - }; - Net.Smtp.Send(data); -} - -func DB.CreateUser(email, name, password) -{ - var res = {Success = true, Redirect="/"}; - DB.Lock(); - var dbCon = DB.Open(); - if(Sqlite.Exec(dbCon, "SELECT * FROM accounts LIMIT 1;").Length == 0) - { - //create the admin - - var salt = Crypto.RandomBytes(32, "CPKG"); - var hash = Crypto.PBKDF2(password, salt, DB.ITTR,64,384); - - var r = Sqlite.Exec(dbCon,$"INSERT INTO accounts (email, accountName, password_hash, password_salt, flags) values ({Sqlite.Escape(email)},{Sqlite.Escape(name)},{Sqlite.Escape(Crypto.Base64Encode(hash))},{Sqlite.Escape(Crypto.Base64Encode(salt))},{DB.FLAG_ADMIN|DB.FLAG_VERIFIED});"); - if(TypeOf(r) == "String") res = {Success = false, Reason = r}; - - } - else - { - var exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE email = {Sqlite.Escape(email)};"); - if(TypeOf(exec) == "List" && exec.Length > 0) - { - - exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE accountName = {Sqlite.Escape(name)};"); - if(TypeOf(exec) == "List" && exec.Length > 0) - { - res = {Success=false, Reason = "Email and Name already exists"}; - } - else - { - res = {Success=false, Reason = "Email already exists"}; - } - } - else { - exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE accountName = {Sqlite.Escape(name)};"); - if(TypeOf(exec) == "List" && exec.Length > 0) - { - res = {Success=false, Reason = "Name already exists"}; - } - else if(DB.Config.AllowRegister) - { - //email and name already exists - var salt = Crypto.RandomBytes(32, "CPKG"); - var hash = Crypto.PBKDF2(password, salt, DB.ITTR,64,384); - - var verify_hash = Crypto.RandomBytes(32, "CPKG"); - var verify_hash_str = Crypto.Base64Encode(verify_hash); - - var r = Sqlite.Exec(dbCon,$"INSERT INTO accounts (email, accountName, password_hash, password_salt, verifyKey, flags) values ({Sqlite.Escape(email)},{Sqlite.Escape(name)},{Sqlite.Escape(Crypto.Base64Encode(hash))},{Sqlite.Escape(Crypto.Base64Encode(salt))},{Sqlite.Escape(verify_hash_str)},{DB.FLAG_VERIFY});"); - if(TypeOf(r) == "String") res = {Success = false, Reason = r}; - - if(DB.Config.MailConfig) - DB.SendVerifyEmail(email,name, verify_hash_str); - - res = {Success=true, Redirect="/check_email"}; - } - else - { - res = {Success=false, Reason="Registration is disabled on this server"}; - } - } - } - Sqlite.Close(dbCon); - DB.Unlock(); - return res; -} -func DB.GetPackageVersions(name) -{ - var sql = $"SELECT * FROM packages p inner join versions v on p.id = v.packageId inner join accounts a on p.accountId = a.id WHERE p.packageName = {Sqlite.Escape(name)} ORDER BY v.version DESC;"; - var dbCon = DB.Open(); - var res = Sqlite.Exec(dbCon, sql); - - Sqlite.Close(dbCon); - DB.Unlock(); - - var list = []; - each(var item : res) - { - var version = Version.FromLong(ParseLong(item.version)).ToString(); - var uploadTime = ParseLong(item.uploadTime); - list.Add({ - version, - download = $"./api/v1/download?name={Net.Http.UrlEncode(name)}&version={Net.Http.UrlEncode(version)}", - accountName = item.accountName, - description = item.description, - maintainer = item.maintainer, - type = item.type, - repo = item.repo, - homepage = item.homepage, - license = item.license, - uploadTime, - uploadDate=Time.UTCUsSlashDate(uploadTime) - }); - } - return list; -} -func DB.QueryPackages(q, offset, limit) -{ - if(TypeOf(offset) != "Long") offset = 0; - if(TypeOf(limit) != "Long") limit = 20; - var q2 = Sqlite.Escape($"%{q}%"); - var sql = $"SELECT * FROM packages p inner join versions v on p.id = v.packageId and (p.packageName LIKE {q2} OR v.description LIKE {q2}) and v.version = (SELECT MAX(version) FROM versions WHERE packageId = v.packageId) INNER JOIN accounts a on a.id = p.accountId LIMIT {limit} OFFSET {offset};"; - DB.Lock(); - var dbCon = DB.Open(); - var res = Sqlite.Exec(dbCon, sql); - Sqlite.Close(dbCon); - DB.Unlock(); - - var res2=[]; - each(var item : res) - { - var uploadTime = ParseLong(item.uploadTime); - res2.Add({ - accountName = item.accountName, - description = item.description, - homepage = item.homepage, - license = item.license, - maintainer = item.maintainer, - packageName = item.packageName, - uploadTime, - uploadDate = Time.UTCUsSlashDate(uploadTime), - repo = item.repo, - type = item.type, - version = Version.FromLong(ParseLong(item.version)).ToString() - }); - } - return res2; -} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/backend/email.tcross b/Tesses.CrossLang.PackageServer/src/backend/email.tcross new file mode 100644 index 0000000..7fed1b8 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/backend/email.tcross @@ -0,0 +1,171 @@ +func DB.SendVerifyEmail(email,name, verify_hash_str) +{ + var emailText = $" +

Hello {Net.Http.HtmlEncode(name)}

+ To verify your account go to {DB.Config.Prefix.TrimEnd('/')}/verify?code={Net.Http.UrlEncode(verify_hash_str)} + "; + var data = { + server = DB.Config.MailConfig.Server, + auth = DB.Config.MailConfig.Auth, + domain = DB.Config.MailConfig.Domain, + from = DB.Config.MailConfig.From, + to = email.Replace(">","").Replace("\n",""), + subject = "Verify your account on CPKG", + body = { + type = "text/html", + data = emailText + } + }; + Net.Smtp.Send(data); +} +func DB.SendForgotPasswordEmail(email,name, verify_hash_str) +{ + var emailText = $" +

Hello {Net.Http.HtmlEncode(name)}

+

This will expire in 10 minutes

+ To reset your password go to {DB.Config.Prefix.TrimEnd('/')}/new_password?code={Net.Http.UrlEncode(verify_hash_str)} + "; + var data = { + server = DB.Config.MailConfig.Server, + auth = DB.Config.MailConfig.Auth, + domain = DB.Config.MailConfig.Domain, + from = DB.Config.MailConfig.From, + to = email.Replace(">","").Replace("\n",""), + subject = "Reset your password for CPKG", + body = { + type = "text/html", + data = emailText + } + }; + Net.Smtp.Send(data); +} +func DB.UnforgetPassword(verifyKey, password, confirm) +{ + if(password != confirm) return {Success=false, Reason = "Passwords do not match."}; + + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE verifyKey = {Sqlite.Escape(verifyKey)};"); + if(exec.Length == 1) + { + var flags = ParseLong(exec[0].flags); + var verifyExpire = ParseLong(exec[0].verifyExpire); + var now = DateTime.NowEpoch; + + if((flags & DB.FLAG_RESETPASS) == 0) { + Sqlite.Close(dbCon); + DB.Unlock(); + return {Success=false,Reason="Invalid or expired verification code"}; + } + + if(now < verifyExpire) + { + flags &= ~DB.FLAG_VERIFY; + flags |= DB.FLAG_VERIFIED; + flags &= ~DB.FLAG_RESETPASS; + var salt = Crypto.RandomBytes(32, "CPKG"); + var hash = Crypto.PBKDF2(password, salt, DB.ITTR,64,384); + + Sqlite.Exec(dbCon,$"UPDATE accounts SET flags = {flags}, password_hash = {Sqlite.Escape(Crypto.Base64Encode(hash))}, password_salt = {Sqlite.Escape(Crypto.Base64Encode(salt))} WHERE id = {exec[0].id};"); + Sqlite.Close(dbCon); + DB.Unlock(); + return { Success=true }; + } + + Sqlite.Close(dbCon); + DB.Unlock(); + return {Success=false, Reason="Invalid or expired verification code"}; + } + else { + Sqlite.Close(dbCon); + DB.Unlock(); + + return {Success=false, Reason="Invalid or expired verification code"}; + } +} +func DB.ForgotPassword(email) +{ + if(TypeOf(DB.Config.MailConfig) != "Dictionary") return {Success=false, Reason="This server can't send emails"}; + + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE email = {Sqlite.Escape(email)};"); + var res={Success=false, Reason="Could not find account"}; + if(TypeOf(exec) != "String" && exec.Length == 1) + { + Console.WriteLine(exec); + var name = exec[0].accountName; + + var flags = ParseLong(exec[0].flags); + + + var verify_hash = Crypto.RandomBytes(32, "CPKG"); + var verify_hash_str = Crypto.Base64Encode(verify_hash); + + + flags |= DB.FLAG_RESETPASS; + + exec = Sqlite.Exec(dbCon,$"UPDATE accounts SET flags = {flags}, verifyKey = {Sqlite.Escape(verify_hash_str)}, verifyExpire = {DateTime.NowEpoch+600} WHERE id = {exec[0].id};"); + + if(TypeOf(exec) == "String") {res = {Success = false, Reason = r};} + else + { + if(DB.Config.MailConfig) + DB.SendForgotPasswordEmail(email,name, verify_hash_str); + + res = {Success=true, Redirect="/check_email"}; + } + + + //DB.FLAG_RESETPASS + } + Sqlite.Close(dbCon); + DB.Unlock(); + return res; +} +func DB.VerifyEmail(verifyKey) +{ + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM accounts WHERE verifyKey = {Sqlite.Escape(verifyKey)};"); + if(exec.Length == 1) + { + var flags = ParseLong(exec[0].flags); + var verifyExpire = ParseLong(exec[0].verifyExpire); + var now = DateTime.NowEpoch; + + if((flags & DB.FLAG_RESETPASS) != 0) { + Sqlite.Close(dbCon); + DB.Unlock(); + return {Success=false,Reason="Invalid or expired verification code"}; + } + + if((flags & DB.FLAG_VERIFIED) != 0) { + Sqlite.Close(dbCon); + DB.Unlock(); + return { Success=true }; + } + + + if(now < verifyExpire) + { + flags &= ~DB.FLAG_VERIFY; + flags |= DB.FLAG_VERIFIED; + Sqlite.Exec(dbCon,$"UPDATE accounts SET flags = {flags} WHERE id = {exec[0].id};"); + Sqlite.Close(dbCon); + DB.Unlock(); + return { Success=true }; + } + + Sqlite.Close(dbCon); + DB.Unlock(); + return {Success=false, Reason="Invalid or expired verification code"}; + } + else { + Sqlite.Close(dbCon); + DB.Unlock(); + + return {Success=false, Reason="Invalid or expired verification code"}; + } + +} diff --git a/Tesses.CrossLang.PackageServer/src/backend/package.tcross b/Tesses.CrossLang.PackageServer/src/backend/package.tcross new file mode 100644 index 0000000..8314d29 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/backend/package.tcross @@ -0,0 +1,302 @@ +func DB.CanUploadPackagePrefix(userId, packageName) +{ + var prefix = packageName.Split(".",true,2); + if(prefix.Length >= 1) + prefix = prefix[0]; + else return false; + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon, $"SELECT * FROM reserved_prefixes WHERE prefix = {Sqlite.Escape(prefix)};"); + + Sqlite.Close(dbCon); + DB.Unlock(); + + if(TypeOf(exec) == "List" && exec.Length > 0) return ParseLong(exec[0].accountId) == userId; + + return true; +} + + +func DB.PackageExists(userId,pkgInfo) +{ + var statusCode = 4; + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * from packages WHERE packageName = {Sqlite.Escape(pkgInfo.Name)};"); + if(TypeOf(exec) == "List") + { + if(exec.Length == 1) + { + var pkgId = exec[0].id; + if(ParseLong(exec[0].accountId) != userId) + statusCode=4; + else + { + exec = Sqlite.Exec(dbCon,$"SELECT * from versions WHERE packageId = {pkgId} AND version = {pkgInfo.Version.VersionInt};"); + if(TypeOf(exec) == "List") + { + if(exec.Length == 1) + { + if(pkgInfo.Version.Stage == "dev") + statusCode=3; + else + statusCode=2; + } + else statusCode=1; + } + } + } + else statusCode=0; + } + + Sqlite.Close(dbCon); + DB.Unlock(); + return statusCode; +} +func DB.AddPackage(userId,pkgInfo) +{ + DB.Lock(); + var dbCon = DB.Open(); + Sqlite.Exec(dbCon,$"INSERT INTO packages (packageName, accountId) VALUES ({Sqlite.Escape(pkgInfo.Name)}, {userId});"); + Sqlite.Close(dbCon); + DB.Unlock(); + +} +func DB.GetPackageFileData(name, version) +{ + if(TypeOf(name) != "String") name = ""; + if(TypeOf(version) != "String") version = ""; + + var file = DB.working / "Packages" / name / $"{name}-{version}.crvm"; + + + if(FS.Local.FileExists(file) && name.Length > 0 && version.Length > 0) + { + var strm = FS.Local.OpenFile(file,"rb"); + + if(strm != null) + { + var exec = VM.LoadExecutable(strm); + strm.Close(); + return exec; + } + } + return null; +} +func DB.GetPackageIcon(name, version) +{ + var file = DB.GetPackageFileData(name, version); + if(file != null) + { + if(file.Icon != null) + { + return file.Icon; + } + } + + return embed("crosslang.png"); +} +func DB.UploadPackage(userId, filePath) +{ + if(!FS.Local.FileExists(filePath)) return {Success=false, Reason = "File does not exist"}; + var userInfo = DB.GetUserInfo(userId); + if(userInfo == null) return { Success=false, Reason = "User does not exist"}; + + if((userInfo.flags & DB.FLAG_VERIFIED) == 0) return { Success=false, Reason = "User is not verified"}; + + var strm = FS.Local.OpenFile(filePath,"rb"); + var failed=false; + var reason = ""; + try { + var pkgInfo = VM.LoadExecutable(strm); + }catch(ex) { + if(ex.Type == "NativeException") + reason = ex.Text; + + failed=true; + } + + strm.Close(); + if(failed) return {Success=false, Reason = reason}; + if(!DB.CanUploadPackagePrefix(userId,pkgInfo.Name)) return { Success = false, Reason = "You can't upload a package with that prefix."}; + + switch(DB.PackageExists(userId, pkgInfo)) + { + case 0: + //package name does not exist + DB.AddPackage(userId,pkgInfo); + case 1: + DB.AddVersion(pkgInfo); + FS.Local.CreateDirectory(DB.working / "Packages" / pkgInfo.Name); + + FS.Local.MoveFile(filePath, DB.working / "Packages" / pkgInfo.Name / $"{pkgInfo.Name}-{pkgInfo.Version}.crvm"); + + + //package version does not exist + break; + case 2: + return { Success = false, Reason = "Package already exists and is not a dev package." }; + case 3: + //update package version (it exists but is dev) + + DB.UpdateVersion(pkgInfo); + FS.Local.MoveFile(filePath, DB.working / "Packages" / pkgInfo.Name / $"{pkgInfo.Name}-{pkgInfo.Version}.crvm"); + break; + case 4: + //package is not yours + return { Success = false, Reason = "You don't own the package."}; + } + + return {Success=true}; +} +func DB.QueryPackages(q, offset, limit,$account) +{ + if(TypeOf(offset) != "Long") offset = 0; + if(TypeOf(limit) != "Long") limit = 20; + var q2 = Sqlite.Escape($"%{q}%"); + + var accountId = -1; + + if(TypeOf(account) == "String") + { + DB.Lock(); + var dbCon = DB.Open(); + var res = Sqlite.Exec(dbCon, $"SELECT * FROM accounts WHERE accountName = {Sqlite.Escape(account)};"); + Sqlite.Close(dbCon); + DB.Unlock(); + if(res.Length > 0) + { + accountId = ParseLong(res[0].id); + } + else { + accountId = 0; + } + } + + var sql = accountId > -1 ? $"SELECT * FROM packages p inner join versions v on p.id = v.packageId and (p.packageName LIKE {q2} OR v.description LIKE {q2}) and v.version = (SELECT MAX(version) FROM versions WHERE packageId = v.packageId) INNER JOIN accounts a on a.id = p.accountId WHERE p.accountId == {accountId} LIMIT {limit} OFFSET {offset};" : $"SELECT * FROM packages p inner join versions v on p.id = v.packageId and (p.packageName LIKE {q2} OR v.description LIKE {q2}) and v.version = (SELECT MAX(version) FROM versions WHERE packageId = v.packageId) INNER JOIN accounts a on a.id = p.accountId LIMIT {limit} OFFSET {offset};"; + DB.Lock(); + var dbCon = DB.Open(); + var res = Sqlite.Exec(dbCon, sql); + Sqlite.Close(dbCon); + DB.Unlock(); + + var res2=[]; + each(var item : res) + { + var uploadTime = ParseLong(item.uploadTime); + res2.Add({ + accountName = item.accountName, + description = item.description, + homepage = item.homepage, + license = item.license, + maintainer = item.maintainer, + packageName = item.packageName, + uploadTime, + uploadDate = new DateTime(uploadTime).ToString("%Y/%m/%d %H:%M:%S UTC"), + repo = item.repo, + type = item.type, + version = Version.FromLong(ParseLong(item.version)).ToString() + }); + } + return res2; +} + +func DB.GetPackages($page) +{ + if(TypeOf(page) != "Long") page = 1; + + var sql = "SELECT * FROM packages p join versions v on p.id = v.packageId and v.version = (SELECT MAX(version) FROM versions WHERE packageId = v.packageId);"; + DB.Lock(); + var dbCon = DB.Open(); + var res = Sqlite.Exec(dbCon, sql); + + + Sqlite.Close(dbCon); + DB.Unlock(); + var res2 = []; + each(var item : res) + { + res2.Add({ + id = item.packageId, + name = item.packageName, + version = Version.FromLong(ParseLong(item.version)).ToString(), + homepage = item.homepage, + repo = item.repo, + type = item.type, + maintainer = item.maintainer, + description = item.description + }); + } + return res2; +} +func DB.RemovePrefix(userName, prefix) +{ + + if(TypeOf(prefix) != "String" || prefix.Length == 0) return {Success=false, Reason="Invalid prefix"}; + var user = DB.GetAccountInfo(userName); + if(TypeOf(user) != "Dictionary") return {Success=false, Reason = "Username not found"}; + if((user.flags & DB.FLAG_VERIFIED) == 0) return {Sucess=false, Reason="Account is not verified"}; + + DB.Lock(); + + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"DELETE FROM reserved_prefixes WHERE accountId == {user.id} AND prefix == {Sqlite.Escape(prefix)};"); + + + Sqlite.Close(dbCon); + DB.Unlock(); + + if(TypeOf(exec) == "String") return {Success = false, Reason = exec}; + + return {Success=true}; +} +func DB.CreatePrefix(userName, prefix) +{ + if(TypeOf(prefix) != "String" || prefix.Length == 0) return {Success=false, Reason="Invalid prefix"}; + var user = DB.GetAccountInfo(userName); + if(TypeOf(user) != "Dictionary") return {Success=false, Reason = "Username not found"}; + if((user.flags & DB.FLAG_VERIFIED) == 0) return {Sucess=false, Reason="Account is not verified"}; + + DB.Lock(); + + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"INSERT INTO reserved_prefixes (accountId,prefix) values ({user.id},{Sqlite.Escape(prefix)});"); + + + Sqlite.Close(dbCon); + DB.Unlock(); + + if(TypeOf(exec) == "String") return {Success = false, Reason = exec}; + + return {Success=true}; +} + +func DB.QueryReservedPrefixes(name) +{ + var user = DB.GetAccountInfo(name); + if(TypeOf(user) != "Dictionary") return {Success=false, Reason = "Username not found"}; + + DB.Lock(); + + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon, $"SELECT * FROM reserved_prefixes WHERE accountId == {user.id};"); + + Sqlite.Close(dbCon); + + DB.Unlock(); + if(TypeOf(exec) == "String") return { + Success=false, + Reason=exec + }; + + var items = []; + each(var item : exec) + { + items.Add(item.prefix); + } + + return { + Success=true, + Items = items + }; +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/backend/packageversion.tcross b/Tesses.CrossLang.PackageServer/src/backend/packageversion.tcross new file mode 100644 index 0000000..0d8fa21 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/backend/packageversion.tcross @@ -0,0 +1,122 @@ +func DB.GetLatestVersion(name) +{ + var sql = $"SELECT * FROM packages p join versions v on p.id = v.packageId and v.version = (SELECT MAX(version) FROM versions WHERE packageId = v.packageId) and p.packageName = {Sqlite.Escape(name)};"; + DB.Lock(); + var dbCon = DB.Open(); + var res = Sqlite.Exec(dbCon, sql); + DB.Unlock(); + + if(TypeOf(res) == "List" && res.Length > 0) + { + return Version.FromLong(ParseLong(res[0].version)).ToString(); + } + return null; +} +func DB.GetPackageVersions(name) +{ + var sql = $"SELECT * FROM packages p inner join versions v on p.id = v.packageId inner join accounts a on p.accountId = a.id WHERE p.packageName = {Sqlite.Escape(name)} ORDER BY v.version DESC;"; + var dbCon = DB.Open(); + var res = Sqlite.Exec(dbCon, sql); + + Sqlite.Close(dbCon); + DB.Unlock(); + + var list = []; + each(var item : res) + { + var version = Version.FromLong(ParseLong(item.version)).ToString(); + var uploadTime = ParseLong(item.uploadTime); + list.Add({ + version, + download = $"./api/v1/download?name={Net.Http.UrlEncode(name)}&version={Net.Http.UrlEncode(version)}", + docs = $"./package_docs?name={Net.Http.UrlEncode(name)}&version={Net.Http.UrlEncode(version)}", + accountName = item.accountName, + description = item.description, + maintainer = item.maintainer, + type = item.type, + repo = item.repo, + homepage = item.homepage, + license = item.license, + uploadTime, + uploadDate=new DateTime(uploadTime).ToString("%Y/%m/%d %H:%M:%S UTC") + }); + } + return list; +} +func DB.UpdateVersion(pkgInfo) +{ + + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM packages WHERE packageName = {Sqlite.Escape(pkgInfo.Name)};"); + var pkgId = 0; + + if(TypeOf(exec) == "List" && exec.Length == 1) + { + pkgId = exec[0].id; + } + var version = pkgInfo.Version.VersionInt; + var info = Json.Decode(pkgInfo.Info); + var description = info.description; + var type = info.type; + var maintainer = info.maintainer; + var homepage = info.homepage; + var repo = info.repo; + var license = info.license; + if(TypeOf(description) != "String") description=""; + + if(TypeOf(type) != "String") type=""; + + if(TypeOf(maintainer) != "String") maintainer=""; + + if(TypeOf(homepage) != "String") homepage=""; + + if(TypeOf(repo) != "String") repo=""; + + if(TypeOf(license) != "String") license=""; + + //CREATE TABLE IF NOT EXISTS versions (id INTEGER PRIMARY KEY AUTOINCREMENT, packageId INTEGER, version INTEGER, description TEXT, type TEXT, maintainer TEXT, homepage TEXT, repo TEXT, license TEXT); + //VALUES ({pkgId},{version},{Sqlite.Escape(description)},{Sqlite.Escape(type)},{Sqlite.Escape(maintainer)},{Sqlite.Escape(homepage)},{Sqlite.Escape(repo)},{Sqlite.Escape(license)}); + Sqlite.Exec(dbCon,$"UPDATE versions SET description = {Sqlite.Escape(description)}, type = {Sqlite.Escape(type)}, maintainer = {Sqlite.Escape(maintainer)}, homepage = {Sqlite.Escape(homepage)}, repo = {Sqlite.Escape(repo)}, license = {Sqlite.Escape(license)}, uploadTime = {DateTime.NowEpoch} WHERE packageId = {pkgId} AND version = {version};"); + Sqlite.Close(dbCon); + DB.Unlock(); + +} +func DB.AddVersion(pkgInfo) +{ + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM packages WHERE packageName = {Sqlite.Escape(pkgInfo.Name)};"); + var pkgId = 0; + + if(TypeOf(exec) == "List" && exec.Length == 1) + { + pkgId = exec[0].id; + } + var version = pkgInfo.Version.VersionInt; + var info = Json.Decode(pkgInfo.Info); + var description = info.description; + var type = info.type; + var maintainer = info.maintainer; + var homepage = info.homepage; + var repo = info.repo; + var license = info.license; + if(TypeOf(description) != "String") description=""; + + if(TypeOf(type) != "String") type=""; + + if(TypeOf(maintainer) != "String") maintainer=""; + + if(TypeOf(homepage) != "String") homepage=""; + + if(TypeOf(repo) != "String") repo=""; + + if(TypeOf(license) != "String") license=""; + + //CREATE TABLE IF NOT EXISTS versions (id INTEGER PRIMARY KEY AUTOINCREMENT, packageId INTEGER, version INTEGER, description TEXT, type TEXT, maintainer TEXT, homepage TEXT, repo TEXT, license TEXT); + + Sqlite.Exec(dbCon,$"INSERT INTO versions (packageId,version,description,type,maintainer,homepage,repo,license,uploadTime) VALUES ({pkgId},{version},{Sqlite.Escape(description)},{Sqlite.Escape(type)},{Sqlite.Escape(maintainer)},{Sqlite.Escape(homepage)},{Sqlite.Escape(repo)},{Sqlite.Escape(license)},{DateTime.NowEpoch});"); + Sqlite.Close(dbCon); + DB.Unlock(); + +} diff --git a/Tesses.CrossLang.PackageServer/src/backend/session.tcross b/Tesses.CrossLang.PackageServer/src/backend/session.tcross new file mode 100644 index 0000000..969a63e --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/backend/session.tcross @@ -0,0 +1,72 @@ +func DB.GetUserIdFromSession(session) +{ + DB.Lock(); + var dbCon = DB.Open(); + var exec = Sqlite.Exec(dbCon,$"SELECT * FROM sessions WHERE key = {Sqlite.Escape(session)};"); + + Sqlite.Close(dbCon); + DB.Unlock(); + + if(TypeOf(exec) == "List" && exec.Length == 1) return ParseLong(exec[0].accountId); + + return -1; +} +func DB.GetSessionFromBearer(ctx) +{ + var auth = ctx.RequestHeaders.TryGetFirst("Authorization"); + if(TypeOf(auth) == "String") + { + auth=auth.Split(" ",true,2); + if(auth.Length < 2) return null; + if(auth[0] != "Bearer") return null; + var uid = DB.GetUserIdFromSession(auth[1]); + if(uid != -1) return auth[1]; + } + return null; +} +func DB.GetSession(ctx) +{ + var cookie = ctx.RequestHeaders.TryGetFirst("Cookie"); + if(TypeOf(cookie) == "String") + { + each(var part : cookie.Split("; ")) + { + if(part.Length > 0) + { + var cookieKV = part.Split("=",true,2); + if(cookieKV.Length == 2 && cookieKV[0] == "Session") + { + var session = cookieKV[1]; + var sessionId = DB.GetUserIdFromSession(session); + + if(sessionId != -1) + return session; + } + } + } + } + return null; +} +func DB.CreateSession(userId) +{ + DB.Lock(); + var dbCon = DB.Open(); + + var rand = Net.Http.UrlEncode(Crypto.Base64Encode(Crypto.RandomBytes(32, "CPKG"))); + Sqlite.Exec(dbCon, $"INSERT INTO sessions (accountId,key) VALUES ({userId},{Sqlite.Escape(rand)});"); + Sqlite.Close(dbCon); + DB.Unlock(); + return rand; +} +func DB.DestroySession(session) +{ + if(TypeOf(session) != "String") return false; + DB.Lock(); + var dbCon = DB.Open(); + var res = Sqlite.Exec(dbCon, $"DELETE FROM sessions WHERE key = {Sqlite.Escape(session)};"); + Sqlite.Close(dbCon); + DB.Unlock(); + if(TypeOf(res) == "String") return false; + + return true; +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/components/docclass.tcross b/Tesses.CrossLang.PackageServer/src/components/docclass.tcross new file mode 100644 index 0000000..da61803 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/components/docclass.tcross @@ -0,0 +1,32 @@ +func Components.DocClass(cls) +{ + var namePart = $"class {cls.Name}"; + + if(cls.Inherits != "ClassObject") + namePart += $" : {cls.Inherits}"; + + + return
+ 0)> + + {"/^"} + ))> + {"^/"} +
+
+ + {namePart} + {"{"} + + {"}"} +
; + +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/components/docclassentry.tcross b/Tesses.CrossLang.PackageServer/src/components/docclassentry.tcross new file mode 100644 index 0000000..a6c5ce2 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/components/docclassentry.tcross @@ -0,0 +1,35 @@ +func Components.DocClassEntry(ent) +{ + var mod = ent.Modifier; + var args = ";"; + if(ent.IsAbstract) + { + mod += " abstract"; + } + if(ent.IsFunction) + { + args = "("; + + for(var i = 0; i < ent.Arguments.Length; i++) + { + if(i > 0) args += ", "; + args += ent.Arguments[i]; + } + + args += ")"; + } + + return
+ 0)> + + {"/^"} + ))> + {"^/"} +
+
+ + + {mod} {ent.Name}{args} + +
; +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/components/docfunction.tcross b/Tesses.CrossLang.PackageServer/src/components/docfunction.tcross new file mode 100644 index 0000000..ec970aa --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/components/docfunction.tcross @@ -0,0 +1,30 @@ +func Components.DocFunction(fn) +{ + var fnText = "func "; + for(var i = 0; i < fn.NameParts.Length; i++) + { + if(i > 0) fnText += "."; + fnText += fn.NameParts[i]; + } + fnText += "("; + for(var i = 0; i < fn.Arguments.Length; i++) + { + if(i > 0) fnText += ", "; + fnText += fn.Arguments[i]; + } + fnText += ")"; + + return
+ 0)> + + {"/^"} + ))> + {"^/"} +
+
+ + + {fnText} + +
; +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/components/prefix.tcross b/Tesses.CrossLang.PackageServer/src/components/prefix.tcross new file mode 100644 index 0000000..72e60b6 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/components/prefix.tcross @@ -0,0 +1,19 @@ +func Components.Prefix(prefixName,isMine,owner,csrf) +{ + return + + + +
+ {prefixName} + + + + +
+
+ + {prefixName} + + ; +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/pages/account.tcross b/Tesses.CrossLang.PackageServer/src/pages/account.tcross index b909d1f..a96484e 100644 --- a/Tesses.CrossLang.PackageServer/src/pages/account.tcross +++ b/Tesses.CrossLang.PackageServer/src/pages/account.tcross @@ -81,7 +81,7 @@ func Pages.Account(ctx)

{user.accountName}

- Packages|Reserved Prefixes + Packages|Reserved Prefixes @@ -94,6 +94,11 @@ func Pages.Account(ctx) + + + Admin + + Logout diff --git a/Tesses.CrossLang.PackageServer/src/pages/admin.tcross b/Tesses.CrossLang.PackageServer/src/pages/admin.tcross new file mode 100644 index 0000000..51e8754 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/pages/admin.tcross @@ -0,0 +1,235 @@ +func Pages.Admin(ctx) +{ + + var active = DB.LoginButton(ctx,false,""); + var csrf=""; + + + var pages = [ + { + active = false, + route = "/packages", + text = "Packages" + }, + { + active = false, + route = "/upload", + text = "Upload" + }, + active + ]; + if(!active.admin) ctx.StatusCode = 401; + else csrf = DB.CreateCSRF(ctx); + + if(ctx.Method == "POST") + { + var csrf2 = ctx.QueryParams.TryGetFirst("csrf"); + var action2 = ctx.QueryParams.TryGetFirst("action"); + if(TypeOf(action2) != "String") {ctx.StatusCode = 501; return Shell("Unsupported operation", pages,

Unsupported Operation

);} + if(!active.admin) {ctx.StatusCode = 401; return Shell("Not an admin", pages,

Not an admin

);} + if(TypeOf(csrf2) != "String") {ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,

Invalid CSRF

);} + if(DB.VerifyCSRF(active.session, csrf2)) + { + //we have authorization + switch(action2) + { + case "server_config": + { + var prefix = ctx.QueryParams.TryGetFirst("prefix"); + var port = ctx.QueryParams.TryGetFirstInt("port"); + var allowRegistering = ctx.QueryParams.GetFirstBoolean("allowRegistering"); + if(TypeOf(prefix) == "String" && TypeOf(port) == "Long" && port >= 1 && port <= 65534) + { + DB.Config.Prefix = prefix; + DB.Config.Port = port; + DB.Config.AllowRegister = allowRegistering; + FS.WriteAllText(FS.Local, DB.working / "conf.json", Json.Encode(DB.Config,true)); + } + else { + ctx.StatusCode = 400; + return Shell("Invalid input", pages,

Invalid Input

); + } + } + break; + case "smtp_config": + { + var host = ctx.QueryParams.TryGetFirst("host"); + var port = ctx.QueryParams.TryGetFirstInt("port"); + var tls = ctx.QueryParams.GetFirstBoolean("tls"); + var username = ctx.QueryParams.TryGetFirst("username"); + var password = ctx.QueryParams.TryGetFirst("password"); + var email = ctx.QueryParams.TryGetFirst("email"); + var fromname = ctx.QueryParams.TryGetFirst("fromname"); + var domain = ctx.QueryParams.TryGetFirst("domain"); + if(TypeOf(host) == "String" && TypeOf(username) == "String" && TypeOf(username) == "String" && TypeOf(password) == "String" && TypeOf(email) == "String" && TypeOf(fromname) == "String" && TypeOf(domain) == "String" && TypeOf(port) == "Long" && port >= 0 && port <= 65534) + { + if(TypeOf(DB.Config.MailConfig) != "Dictionary") + DB.Config.MailConfig={}; + if(TypeOf(DB.Config.MailConfig.Server) != "Dictionary") + DB.Config.MailConfig.Server={}; + if(TypeOf(DB.Config.MailConfig.Auth) != "Dictionary") + DB.Config.MailConfig.Auth={}; + if(TypeOf(DB.Config.MailConfig.From) != "Dictionary") + DB.Config.MailConfig.From={}; + + DB.Config.MailConfig.Server.host = host; + DB.Config.MailConfig.Server.port = port == 0 ? undefined : port; + DB.Config.MailConfig.Server.tls = tls; + + DB.Config.MailConfig.Auth.username = username; + + if(password.Length != 0) + DB.Config.MailConfig.Auth.password = password; + + + DB.Config.MailConfig.Domain = domain; + DB.Config.MailConfig.From.email = email; + DB.Config.MailConfig.From.name = fromname; + + FS.WriteAllText(FS.Local, DB.working / "conf.json", Json.Encode(DB.Config,true)); + } + else { + ctx.StatusCode = 400; + return Shell("Invalid input", pages,

Invalid Input

); + } + } + break; + default: + ctx.StatusCode=501; + return Shell($"Unsupported operation {action2}", pages,

Unsupported operation {action2}

); + } + } + else + { + ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,

Invalid CSRF

); + } + } + + + var html =
+ + /* + +
    + +
  • {item.Key}: {item.Value}
  • + +
+
+ */ +
+ Cache Package +
+ + + + + +
+
+
+ Accounts +
+
+ + +
+ +
+ View Accounts + Register Account +
+
+ Server Configuration +
+ + +
+ + +
+
+ + +
+ +
+ + + + + + + + + + + //DB.Config.Prefix + + + +
+ +
+
+
+ SMTP Configuration +
+ + +
+ + +
+
+ + +
+
+ + + + + + + + + + //DB.Config.Prefix + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ +

You are not authorized in the admin panel

+
+
; + + return Shell("Admin", pages,html); +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/pages/admin_register.tcross b/Tesses.CrossLang.PackageServer/src/pages/admin_register.tcross new file mode 100644 index 0000000..00415cb --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/pages/admin_register.tcross @@ -0,0 +1,96 @@ +func Pages.AdminRegister(ctx) +{ + var active = DB.LoginButton(ctx,false,""); + var csrf=""; + + + var pages = [ + { + active = false, + route = "/packages", + text = "Packages" + }, + { + active = false, + route = "/upload", + text = "Upload" + }, + active + ]; + if(!active.admin) ctx.StatusCode = 401; + else csrf = DB.CreateCSRF(ctx); + + if(ctx.Method == "POST") + { + var csrf2 = ctx.QueryParams.TryGetFirst("csrf"); + if(!active.admin) {ctx.StatusCode = 401; return Shell("Not an admin", pages,

Not an admin

);} + if(TypeOf(csrf2) != "String") {ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,

Invalid CSRF

);} + if(DB.VerifyCSRF(active.session, csrf2)) + { + var email = ctx.QueryParams.TryGetFirst("email"); + var displayName = ctx.QueryParams.TryGetFirst("displayName"); + var password = ctx.QueryParams.TryGetFirst("password"); + var confirm = ctx.QueryParams.TryGetFirst("confirm"); + var flags = ctx.QueryParams.GetFirstBoolean("verified") ? DB.FLAG_VERIFIED : DB.FLAG_FLAG_VERIFY; + flags |= (ctx.QueryParams.GetFirstBoolean("admin") ? DB.FLAG_ADMIN : 0); + + if(TypeOf(email) != "String" || TypeOf(displayName) != "String" || TypeOf(password) != "String" || TypeOf(confirm) != "String") + return Shell("Invalid input",pages,

Invalid input

); + + if(password != confirm) + return Shell("Passwords do not match",pages,

Passwords do not match

); + + var res = DB.CreateUserFromAdmin(email, displayName, password, flags); + + if(!res.Success) + { + return Shell(res.Reason, pages,

{res.Reason}

); + } + } + else + { + ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,

Invalid CSRF

); + } + } + + + var html =
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+ +

You are not authorized in the admin panel

+
+ +
; + + return Shell("Admin Register", pages,html); +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/pages/could_not_reset_password.tcross b/Tesses.CrossLang.PackageServer/src/pages/could_not_reset_password.tcross new file mode 100644 index 0000000..4428103 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/pages/could_not_reset_password.tcross @@ -0,0 +1,20 @@ +func Pages.ResetPasswordFailed(reason) +{ + var active = DB.LoginButton(ctx,false,""); + + + var pages = [ + { + active = false, + route = "/packages", + text = "Packages" + }, + { + active = false, + route = "/upload", + text = "Upload" + }, + active + ]; + return Shell($"Reset Password Failed {reason}", pages,

Reset Password Failed {reason}

); +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/pages/forgot_password.tcross b/Tesses.CrossLang.PackageServer/src/pages/forgot_password.tcross new file mode 100644 index 0000000..ae35531 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/pages/forgot_password.tcross @@ -0,0 +1,27 @@ +func Pages.ForgotPassword(ctx) +{ + var pages = [ + { + active = false, + route = "/packages", + text = "Packages" + }, + { + active = false, + route = "/upload", + text = "Upload" + }, + DB.LoginButton(ctx,true) + ]; + var html =
+
+
+ + +
{"We'll"} never share your email with anyone else.
+
+ +
+
; + return Shell("Forgot Password",pages,html); +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/pages/index.tcross b/Tesses.CrossLang.PackageServer/src/pages/index.tcross index 37a3fa9..d6e9835 100644 --- a/Tesses.CrossLang.PackageServer/src/pages/index.tcross +++ b/Tesses.CrossLang.PackageServer/src/pages/index.tcross @@ -1,5 +1,6 @@ func Pages.Index(ctx) { + var repo = TypeOf(DB.Config.RepoCommitPrefix) == "String" ? DB.Config.RepoCommitPrefix : "https://onedev.site.tesses.net/crosslang/crosslangextras/~files/"; var pages = [ { @@ -14,6 +15,12 @@ func Pages.Index(ctx) }, DB.LoginButton(ctx,false) ]; + var commit = comptime { + return BuildTime.Git.Hash; + }; + + + var html =

Make crosslang development faster and more convenient with CPKG

@@ -23,7 +30,8 @@ func Pages.Index(ctx)
- API + API
+ Commit {commit.Remove(8)}
diff --git a/Tesses.CrossLang.PackageServer/src/pages/login.tcross b/Tesses.CrossLang.PackageServer/src/pages/login.tcross index 089e427..0fb129c 100644 --- a/Tesses.CrossLang.PackageServer/src/pages/login.tcross +++ b/Tesses.CrossLang.PackageServer/src/pages/login.tcross @@ -25,6 +25,7 @@ func Pages.Login(ctx)
Sign up + Forgot Password
; diff --git a/Tesses.CrossLang.PackageServer/src/pages/new_password.tcross b/Tesses.CrossLang.PackageServer/src/pages/new_password.tcross new file mode 100644 index 0000000..1911fa3 --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/pages/new_password.tcross @@ -0,0 +1,38 @@ +func Pages.NewPassword(ctx) +{ + var code = ctx.QueryParams.TryGetFirst("code"); + + var pages = [ + { + active = false, + route = "/packages", + text = "Packages" + }, + { + active = false, + route = "/upload", + text = "Upload" + }, + DB.LoginButton(ctx,true) + ]; + + var html =
+
+ + + +
+ + +
+
+ + +
+ + +
+
; + + return Shell("Reset your password", pages, html); +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/pages/package.tcross b/Tesses.CrossLang.PackageServer/src/pages/package.tcross index cf90ce7..f61b4ae 100644 --- a/Tesses.CrossLang.PackageServer/src/pages/package.tcross +++ b/Tesses.CrossLang.PackageServer/src/pages/package.tcross @@ -113,6 +113,7 @@ func Pages.Package(ctx,name)
  • Latest version: {package[0].version}
  • Type: {package[0].type}
  • Download
  • +
  • View Docs
  • 1)> @@ -153,6 +154,8 @@ func Pages.Package(ctx,name)
  • Uploaded: {new DateTime(package[i].uploadTime).ToString("%Y/%m/%d %H:%M:%S UTC")}
  • Type: {package[i].type}
  • Download
  • + +
  • View Docs
  • diff --git a/Tesses.CrossLang.PackageServer/src/pages/package_docs.tcross b/Tesses.CrossLang.PackageServer/src/pages/package_docs.tcross new file mode 100644 index 0000000..e8907cf --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/pages/package_docs.tcross @@ -0,0 +1,52 @@ +func Pages.PackageDocs(ctx) +{ + var name = ctx.QueryParams.TryGetFirst("name"); + if(TypeOf(name) != "String") name = ""; + var version = ctx.QueryParams.TryGetFirst("version"); + if(TypeOf(version) != "String") version = ""; + var pages = [ + { + active = false, + route = "/packages", + text = "Packages" + }, + { + active = false, + route = "/upload", + text = "Upload" + }, + DB.LoginButton(ctx,false) + ]; + + var res = DB.GetPackageFileData(name,version); + var classes = res.Classes; + var functions = res.Functions; + var chunks = res.Chunks; + Console.WriteLine(classes); + each(var item : functions) + { + item.Arguments = chunks[item.ChunkId].Arguments; + } + + + var html =
    + +

    Functions

    +
      + + +
    • + +
    + +

    Classes

    +
      + + +
    • + +
    +
    ; + + return Shell("Documentation", pages, html); +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/pages/reserved_prefixes.tcross b/Tesses.CrossLang.PackageServer/src/pages/reserved_prefixes.tcross new file mode 100644 index 0000000..9a002fa --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/pages/reserved_prefixes.tcross @@ -0,0 +1,97 @@ +func Pages.ReservedPrefixes(ctx) +{ + var name = ctx.QueryParams.TryGetFirst("name"); + if(TypeOf(name) != "String") name = ""; + var active = DB.LoginButton(ctx,false,name); + var csrf=""; + + var pages = [ + { + active = false, + route = "/packages", + text = "Packages" + }, + { + active = false, + route = "/upload", + text = "Upload" + }, + active + ]; + + var user = DB.GetAccountInfo(name); + + if(!active.active) ctx.StatusCode = 401; + else csrf = DB.CreateCSRF(ctx); + + if(ctx.Method == "POST") + { + var csrf2 = ctx.QueryParams.TryGetFirst("csrf"); + var action2 = ctx.QueryParams.TryGetFirst("action"); + if(TypeOf(action2) != "String") {ctx.StatusCode = 501; return Shell("Unsupported operation", pages,

    Unsupported Operation

    );} + if(!active.active) {ctx.StatusCode = 401; return Shell("Not an", pages,

    Not the correct user

    );} + if(TypeOf(csrf2) != "String") {ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,

    Invalid CSRF

    );} + if(DB.VerifyCSRF(active.session, csrf2)) + { + switch(action2) + { + case "add": + { + var prefix = ctx.QueryParams.TryGetFirst("prefix"); + + var res = DB.CreatePrefix(name, prefix); + if(!res.Success) + { + ctx.StatusCode=403; + return Shell(res.Reason, pages,

    {res.Reason}

    ); + } + } + break; + case "remove": + var prefix = ctx.QueryParams.TryGetFirst("prefix"); + + var res = DB.RemovePrefix(name, prefix); + if(!res.Success) + { + ctx.StatusCode=403; + return Shell(res.Reason, pages,

    {res.Reason}

    ); + } + break; + } + } + } + + var items = DB.QueryReservedPrefixes(name); + + var html =
    + + +
    + + +
    + + +
    + +
    +
    + + + +
      + +
    • + +
    • + +
    +
    + +

    Failed to get prefixes {items.Reason}

    +
    + +
    ; + + return Shell("Reserved Prefixes", pages, html); +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/pages/search.tcross b/Tesses.CrossLang.PackageServer/src/pages/search.tcross index 25a41cb..cf064b3 100644 --- a/Tesses.CrossLang.PackageServer/src/pages/search.tcross +++ b/Tesses.CrossLang.PackageServer/src/pages/search.tcross @@ -1,8 +1,23 @@ func Pages.Packages(ctx) { var q = ctx.QueryParams.TryGetFirst("q"); + var account = ctx.QueryParams.TryGetFirst("account"); var page = ParseLong(ctx.QueryParams.TryGetFirst("page")); + q = TypeOf(q) != "String" ? "" : q; page = TypeOf(page) != "Long" ? 1 : page; + var cur = (page - 1) % 3; + var firstPage = (page-1) - cur; + + + var path = $"./packages?q={Net.Http.UrlEncode(q)}"; + + if(TypeOf(account) == "String") + { + path += $"&account={Net.Http.UrlEncode(account)}"; + } + + + if(TypeOf(q) != "String") q = ""; var pages = [ { @@ -25,6 +40,11 @@ func Pages.Packages(ctx) + + + + +
    @@ -36,9 +56,33 @@ func Pages.Packages(ctx)
    +
      - + -
    ; + + + + ; return Shell("Packages",pages,html); } \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/pages/verify_failed.tcross b/Tesses.CrossLang.PackageServer/src/pages/verify_failed.tcross new file mode 100644 index 0000000..482fcba --- /dev/null +++ b/Tesses.CrossLang.PackageServer/src/pages/verify_failed.tcross @@ -0,0 +1,20 @@ +func Pages.VerificationFailed(reason) +{ + var active = DB.LoginButton(ctx,false,""); + + + var pages = [ + { + active = false, + route = "/packages", + text = "Packages" + }, + { + active = false, + route = "/upload", + text = "Upload" + }, + active + ]; + return Shell($"Verification Failed {reason}", pages,

    Verification Failed {reason}

    ); +} \ No newline at end of file diff --git a/Tesses.CrossLang.PackageServer/src/program.tcross b/Tesses.CrossLang.PackageServer/src/program.tcross index 54da5aa..4d3b960 100644 --- a/Tesses.CrossLang.PackageServer/src/program.tcross +++ b/Tesses.CrossLang.PackageServer/src/program.tcross @@ -8,14 +8,7 @@ func main(args) DB.Init(dir); - //should be a route but its crosslang so we can use mountable - - - mountable.Mount("/package_icon.png", (ctx)=>{ - ctx.ResponseHeaders.SetValue("Content-Type", "image/png"); - ctx.SendBytes(embed("crosslang.png")); - return true; - }); + /* PUT /api/v1/upload Authorization Bearer @@ -29,6 +22,25 @@ func main(args) Net.Http.ListenSimpleWithLoop((ctx)=>{ + if(ctx.Path == "/check_email") + { + ctx.WithMimeType("text/html").SendText(Pages.CheckEmail(ctx)); + } + if(ctx.Path == "/reserved_prefixes") + { + ctx.WithMimeType("text/html").SendText(Pages.ReservedPrefixes(ctx)); + return true; + } + if(ctx.Path == "/admin") + { + ctx.WithMimeType("text/html").SendText(Pages.Admin(ctx)); + return true; + } + if(ctx.Path == "/admin_register") + { + ctx.WithMimeType("text/html").SendText(Pages.AdminRegister(ctx)); + return true; + } if(ctx.Path == "/package") { var name = ctx.QueryParams.TryGetFirst("name"); @@ -36,6 +48,11 @@ func main(args) ctx.WithMimeType("text/html").SendText(Pages.Package(ctx,name)); return true; } + if(ctx.Path == "/package_docs") + { + ctx.WithMimeType("text/html").SendText(Pages.PackageDocs(ctx)); + return true; + } if(ctx.Path == "/packages") { ctx.WithMimeType("text/html").SendText(Pages.Packages(ctx)); @@ -297,6 +314,26 @@ func main(args) } return false; } + if(ctx.Path == "/api/v1/logout") + { + ctx.WithMimeType("application/json").SendText({ + Success=DB.DestroySession(DB.GetSessionFromBearer(ctx)) + }.ToString()); + } + if(ctx.Path == "/logout") + { + if(DB.DestroySession(DB.GetSession(ctx))) + { + ctx.StatusCode = 302; + ctx.ResponseHeaders.SetValue("Location", "/"); + ctx.WriteHeaders(); + return true; + } + else { + ctx.WithMimeType("text/html").SendText(Shell("Not logged in",[],

    Not logged in

    )); + return true; + } + } if(ctx.Path == "/login") { if(ctx.Method == "GET") @@ -337,6 +374,98 @@ func main(args) return true; } } + if(ctx.Path == "/verify") + { + var code = ctx.QueryParams.TryGetFirst("code"); + if(TypeOf(code) == "String") + { + var res = DB.VerifyEmail(code); + if(res.Success) + { + ctx.StatusCode = 302; + ctx.ResponseHeaders.SetValue("Location", "/"); + ctx.WriteHeaders(); + return true; + } + else { + ctx.StatusCode=401; + ctx.WithMimeType("text/html").SendText(Pages.VerificationFailed(res.Reason)); + return true; + } + } + else { + ctx.WithMimeType("text/html").SendText(Pages.VerificationFailed("Requires query parameter code")); + return true; + } + } + if(ctx.Path == "/new_password") + { + if(ctx.Method == "GET") + { + ctx.WithMimeType("text/html").SendText(Pages.NewPassword(ctx)); + return true; + } + else if(ctx.Method == "POST") + { + + var code = ctx.QueryParams.TryGetFirst("code"); + var password = ctx.QueryParams.TryGetFirst("password"); + + var confirm = ctx.QueryParams.TryGetFirst("confirm"); + if(TypeOf(code) == "String" && TypeOf(password) == "String" && TypeOf(confirm) == "String") + { + var res = DB.UnforgetPassword(code,password,confirm); + if(res.Success) + { + ctx.StatusCode = 302; + ctx.ResponseHeaders.SetValue("Location", "/"); + ctx.WriteHeaders(); + return true; + } + else { + ctx.StatusCode=401; + ctx.WithMimeType("text/html").SendText(Pages.ResetPasswordFailed(res.Reason)); + return true; + } + } + else { + ctx.StatusCode = 400; + ctx.WithMimeType("text/html").SendText(Pages.ResetPasswordFailed("Invalid State")); + return true; + } + } + } + if(ctx.Path == "/forgot_password") + { + if(ctx.Method == "GET") + { + ctx.WithMimeType("text/html").SendText(Pages.ForgotPassword(ctx)); + return true; + } + else if(ctx.Method == "POST") + { + var email = ctx.QueryParams.TryGetFirst("email"); + if(TypeOf(email) != "String") + { + ctx.StatusCode = 400; + ctx.SendText("

    You forgot the email buddy

    "); + return true; + } + var res = DB.ForgotPassword(email); + if(!res.Success) + { + ctx.StatusCode = 400; + ctx.SendText($"

    Error: {Net.Http.HtmlEncode(res.Reason)}

    "); + return true; + } + + + ctx.StatusCode = 302; + ctx.ResponseHeaders.SetValue("Location", res.Redirect); + ctx.WriteHeaders(); + return true; + } + } if(ctx.Path == "/signup") { if(ctx.Method == "GET") @@ -386,7 +515,7 @@ func main(args) if(!res.Success) { ctx.StatusCode = 400; - ctx.SendText(%"

    Error: {Net.Http.HtmlEncode(res.Reason)}

    "); + ctx.SendText($"

    Error: {Net.Http.HtmlEncode(res.Reason)}

    "); return true; } diff --git a/Tesses.CrossLang.Reference/cross.json b/Tesses.CrossLang.Reference/cross.json new file mode 100644 index 0000000..021773d --- /dev/null +++ b/Tesses.CrossLang.Reference/cross.json @@ -0,0 +1,8 @@ +{ + "info": { + "type": "lib", + "description": "This is used for reference only, don't include into your project" + }, + "name": "Tesses.CrossLang.Reference", + "version": "1.0.0.0-dev" +} \ No newline at end of file diff --git a/Tesses.CrossLang.Reference/src/runtime_methods/console.tcross b/Tesses.CrossLang.Reference/src/runtime_methods/console.tcross new file mode 100644 index 0000000..9e0748b --- /dev/null +++ b/Tesses.CrossLang.Reference/src/runtime_methods/console.tcross @@ -0,0 +1,5 @@ +/^ Write Text To Standard Output ^/ +func Console.WriteLine(text) +{ + +} \ No newline at end of file diff --git a/Tesses.CrossLang.Reference/src/runtime_methods/env.tcross b/Tesses.CrossLang.Reference/src/runtime_methods/env.tcross new file mode 100644 index 0000000..5bee100 --- /dev/null +++ b/Tesses.CrossLang.Reference/src/runtime_methods/env.tcross @@ -0,0 +1,5 @@ +/^ Get Documents Folder ^/ +func Env.getDocuments() +{ + +} \ No newline at end of file diff --git a/Tesses.CrossLang.Reference/src/runtime_methods/fs.tcross b/Tesses.CrossLang.Reference/src/runtime_methods/fs.tcross new file mode 100644 index 0000000..f89af94 --- /dev/null +++ b/Tesses.CrossLang.Reference/src/runtime_methods/fs.tcross @@ -0,0 +1,10 @@ +/^ Read string contents from file ^/ +func FS.ReadAllText(filesystem, path) +{ + +} +/^ Write string contents to file ^/ +func FS.WriteAllText(filesystem, path, contents) +{ + +} \ No newline at end of file diff --git a/Tesses.CrossLang.Reference/src/runtime_methods/json.tcross b/Tesses.CrossLang.Reference/src/runtime_methods/json.tcross new file mode 100644 index 0000000..04c31b9 --- /dev/null +++ b/Tesses.CrossLang.Reference/src/runtime_methods/json.tcross @@ -0,0 +1,16 @@ +/^ + Decode JSON String to Object +^/ +func Json.Decode(text) +{ + +} + +/^ + Encode Object to JSON String + pretty: true: make pretty, false: compact +^/ +func Json.Encode(obj, $pretty) +{ + +} \ No newline at end of file diff --git a/Tesses.CrossLang.Reference/src/types/list.tcross b/Tesses.CrossLang.Reference/src/types/list.tcross new file mode 100644 index 0000000..1bcade8 --- /dev/null +++ b/Tesses.CrossLang.Reference/src/types/list.tcross @@ -0,0 +1,71 @@ +/^ A list created by using the [] expression ^/ +class List : HeapObject { + /^ Returns an Enumerator for the each statement ^/ + public GetEnumerator() + { + + } + /^ Serializes to JSON ^/ + public ToString() + { + + } + /^ Insert an item at index ^/ + public Insert(index, value) + { + + } + /^ Add an item ^/ + public Add(item) + { + + } + /^ Returns whether the list contains the item ^/ + public Contains(item) + { + + } + /^ Gets the index of the item (optionally starting at an index) returns -1 if not found ^/ + public IndexOf(item, $index) + { + + } + /^ Removes all items that are the same value ^/ + public RemoveAllEqual(item) + { + + } + /^ Removes item at index ^/ + public RemoveAt(index) + { + + } + /^ Removes all items ^/ + public Clear() + { + + } + /^ Gets the item at index, also can use array subscript sugar ^/ + public GetAt(index) + { + + } + /^ Sets the item at index to value, also can use array subscript sugar ^/ + public SetAt(index, value) + { + + } + + /^ Gets the nunber of items ^/ + + public Count() + { + + } + /^ Gets the nunber of items ^/ + + public Length() + { + + } +} \ No newline at end of file diff --git a/Tesses.CrossLang.Reference/src/types/mutex.tcross b/Tesses.CrossLang.Reference/src/types/mutex.tcross new file mode 100644 index 0000000..242205c --- /dev/null +++ b/Tesses.CrossLang.Reference/src/types/mutex.tcross @@ -0,0 +1,24 @@ +class Mutex : HeapObject +{ + /^ Create the mutex ^/ + public Mutex() + { + + } + /^ Lock the mutex ^/ + public Lock() + { + + } + /^ Unlock the mutex ^/ + public Unlock() + { + + } + + /^ Try to lock the mutex ^/ + public TryLock() + { + + } +} \ No newline at end of file diff --git a/Tesses.CrossLang.Reference/src/types/object.tcross b/Tesses.CrossLang.Reference/src/types/object.tcross new file mode 100644 index 0000000..0436af5 --- /dev/null +++ b/Tesses.CrossLang.Reference/src/types/object.tcross @@ -0,0 +1,17 @@ +/^ Base Object ^/ +class Object { + /^ Serializes object to String ^/ + public abstract ToString(); +} +/^ All garbage collected objects inherit from me ^/ +class HeapObject : Object { + +} +/^ All CrossLang classes inherit from me ^/ +class ClassObject : HeapObject { + +} +/^ A non typesafe native object ^/ +class Native : HeapObject { + +} \ No newline at end of file diff --git a/Tesses.CrossLang.Reference/src/types/stream.tcross b/Tesses.CrossLang.Reference/src/types/stream.tcross new file mode 100644 index 0000000..eb109c8 --- /dev/null +++ b/Tesses.CrossLang.Reference/src/types/stream.tcross @@ -0,0 +1,74 @@ +/^ base class for stream ^/ +class Stream : HeapObject { + /^ Can Read be called ^/ + public getCanRead() + { + + } + /^ Can Write be called ^/ + public getCanWrite() + { + + } + /^ Can Seek be called ^/ + public getCanSeek() + { + + } + + /^ Read bytes from stream into buffer ^/ + public Read(buffer, offset, length) + { + + } + /^ Write bytes to stream from buffer ^/ + public Write(buffer, offset, length) + { + + } + /^ Read bytes from stream into buffer, reading multiple times if needed ^/ + public ReadBlock(buffer, offset, length) + { + + } + /^ Write bytes to stream from buffer and ensuring that number of bytes is actually writen ^/ + public WriteBlock(buffer, offset, length) + { + + } + + /^ Write text to stream ^/ + public WriteText(text) + { + + } + /^ Copy to another stream ^/ + public CopyTo(stream,$bufferSize) + { + + } + /^ + Seek to position + whence: + + Stream.Begin: 0 + Stream.Current: 1 + Stream.End: 2 + ^/ + public Seek(pos, whence) + { + + } + /^ Flush to disk ^/ + public Flush() + { + + } + + + /^ Close the stream ^/ + public Close() + { + + } +} \ No newline at end of file diff --git a/Tesses.CrossLang.Reference/src/types/string.tcross b/Tesses.CrossLang.Reference/src/types/string.tcross new file mode 100644 index 0000000..ce947ba --- /dev/null +++ b/Tesses.CrossLang.Reference/src/types/string.tcross @@ -0,0 +1,18 @@ +/^ + The String Type + To Concat a String use the + operator +^/ +class String : Object { + + + /^ Search and replace all ^/ + public Replace(oldStr, newStr) + { + + } + /^ Return a copy of the string ^/ + public ToString() + { + + } +} \ No newline at end of file diff --git a/Tesses.CrossLang.Shell/src/main.tcross b/Tesses.CrossLang.Shell/src/main.tcross index e9008b5..1028fc6 100644 --- a/Tesses.CrossLang.Shell/src/main.tcross +++ b/Tesses.CrossLang.Shell/src/main.tcross @@ -137,7 +137,7 @@ func main(args) env.LockRegister(); env.LoadFileWithDependencies(FS.Local,output); - var myArgs = [output]; + var myArgs = [output.ToString()]; for(var i = 1; i < dd.Arguments.Count; i++) { myArgs.Add(dd.Arguments[i]); @@ -333,7 +333,7 @@ func main(args) env.LockRegister(); env.LoadFileWithDependencies(FS.Local,output); - var myArgs = [output]; + var myArgs = [output.ToString()]; for(var i = 1; i < dd.Arguments.Count; i++) { myArgs.Add(dd.Arguments[i]);