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
");
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]);