mirror of
https://onedev.site.tesses.net/crosslang/crosslangextras
synced 2026-02-08 17:15:45 +00:00
Add reference
This commit is contained in:
@@ -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":
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"info": {
|
||||
"type": "lib"
|
||||
},
|
||||
"version": "1.0.0.0-prod",
|
||||
"name": "CrossLang Runtime Objects (this and the root dictionaries are implcit)"
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
```
|
||||
|
||||
|
||||
@@ -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);
|
||||
```
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -4,5 +4,6 @@
|
||||
"type": "console"
|
||||
},
|
||||
"version": "1.0.0.0-prod",
|
||||
"name": "Tesses.CrossLang.PackageServer"
|
||||
"name": "Tesses.CrossLang.PackageServer",
|
||||
"compTime": "secure"
|
||||
}
|
||||
|
||||
219
Tesses.CrossLang.PackageServer/src/backend/accounts.tcross
Normal file
219
Tesses.CrossLang.PackageServer/src/backend/accounts.tcross
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 = $"
|
||||
<h1>Hello {Net.Http.HtmlEncode(name)}</h1>
|
||||
<span>To verify your account go to <a href=\"{DB.Config.Prefix.TrimEnd('/')}/verify?code={Net.Http.UrlEncode(verify_hash_str)}\">{DB.Config.Prefix.TrimEnd('/')}/verify?code={Net.Http.UrlEncode(verify_hash_str)}</a></span>
|
||||
";
|
||||
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;
|
||||
}
|
||||
171
Tesses.CrossLang.PackageServer/src/backend/email.tcross
Normal file
171
Tesses.CrossLang.PackageServer/src/backend/email.tcross
Normal file
@@ -0,0 +1,171 @@
|
||||
func DB.SendVerifyEmail(email,name, verify_hash_str)
|
||||
{
|
||||
var emailText = $"
|
||||
<h1>Hello {Net.Http.HtmlEncode(name)}</h1>
|
||||
<span>To verify your account go to <a href=\"{DB.Config.Prefix.TrimEnd('/')}/verify?code={Net.Http.UrlEncode(verify_hash_str)}\">{DB.Config.Prefix.TrimEnd('/')}/verify?code={Net.Http.UrlEncode(verify_hash_str)}</a></span>
|
||||
";
|
||||
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 = $"
|
||||
<h1>Hello {Net.Http.HtmlEncode(name)}</h1>
|
||||
<p>This will expire in 10 minutes</p>
|
||||
<span>To reset your password go to <a href=\"{DB.Config.Prefix.TrimEnd('/')}/new_password?code={Net.Http.UrlEncode(verify_hash_str)}\">{DB.Config.Prefix.TrimEnd('/')}/new_password?code={Net.Http.UrlEncode(verify_hash_str)}</a></span>
|
||||
";
|
||||
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"};
|
||||
}
|
||||
|
||||
}
|
||||
302
Tesses.CrossLang.PackageServer/src/backend/package.tcross
Normal file
302
Tesses.CrossLang.PackageServer/src/backend/package.tcross
Normal file
@@ -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
|
||||
};
|
||||
}
|
||||
122
Tesses.CrossLang.PackageServer/src/backend/packageversion.tcross
Normal file
122
Tesses.CrossLang.PackageServer/src/backend/packageversion.tcross
Normal file
@@ -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();
|
||||
|
||||
}
|
||||
72
Tesses.CrossLang.PackageServer/src/backend/session.tcross
Normal file
72
Tesses.CrossLang.PackageServer/src/backend/session.tcross
Normal file
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
func Components.DocClass(cls)
|
||||
{
|
||||
var namePart = $"class {cls.Name}";
|
||||
|
||||
if(cls.Inherits != "ClassObject")
|
||||
namePart += $" : {cls.Inherits}";
|
||||
|
||||
|
||||
return <div>
|
||||
<if(cls.Documentation.Length > 0)>
|
||||
<true>
|
||||
{"/^"}
|
||||
<raw(Net.Http.HtmlEncode(cls.Documentation).Replace("\n",<br>))>
|
||||
{"^/"}
|
||||
<br>
|
||||
</true>
|
||||
</if>
|
||||
<code>{namePart}</code>
|
||||
<code>{"{"}</code>
|
||||
<ul>
|
||||
<each(var item : cls.Entries)>
|
||||
<if(item.Modifier != "private")>
|
||||
<true>
|
||||
<li><raw(Components.DocClassEntry(item))></li>
|
||||
</true>
|
||||
</if>
|
||||
</each>
|
||||
</ul>
|
||||
<code>{"}"}</code>
|
||||
</div>;
|
||||
|
||||
}
|
||||
@@ -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 <div>
|
||||
<if(ent.Documentation.Length > 0)>
|
||||
<true>
|
||||
{"/^"}
|
||||
<raw(Net.Http.HtmlEncode(ent.Documentation).Replace("\n",<br>))>
|
||||
{"^/"}
|
||||
<br>
|
||||
</true>
|
||||
</if>
|
||||
<code>
|
||||
{mod} {ent.Name}{args}
|
||||
</code>
|
||||
</div>;
|
||||
}
|
||||
@@ -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 <div>
|
||||
<if(fn.Documentation.Length > 0)>
|
||||
<true>
|
||||
{"/^"}
|
||||
<raw(Net.Http.HtmlEncode(fn.Documentation).Replace("\n",<br>))>
|
||||
{"^/"}
|
||||
<br>
|
||||
</true>
|
||||
</if>
|
||||
<code>
|
||||
{fnText}
|
||||
</code>
|
||||
</div>;
|
||||
}
|
||||
19
Tesses.CrossLang.PackageServer/src/components/prefix.tcross
Normal file
19
Tesses.CrossLang.PackageServer/src/components/prefix.tcross
Normal file
@@ -0,0 +1,19 @@
|
||||
func Components.Prefix(prefixName,isMine,owner,csrf)
|
||||
{
|
||||
return
|
||||
<if(isMine)>
|
||||
<true>
|
||||
|
||||
<form method="POST" action={$"/reserved_prefixes?name={Net.Http.UrlEncode(owner)}"}>
|
||||
<span>{prefixName}</span>
|
||||
<input type="hidden" name="action" value="remove">
|
||||
<input type="hidden" name="csrf" value={csrf}>
|
||||
<input type="hidden" name="prefix" value={prefixName}>
|
||||
<input type="submit" class="btn btn-danger btn-sm" value="Delete">
|
||||
</form>
|
||||
</true>
|
||||
<false>
|
||||
<span>{prefixName}</span>
|
||||
</false>
|
||||
</if>;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func Pages.Account(ctx)
|
||||
<if(TypeOf(user) == "Dictionary")>
|
||||
<true>
|
||||
<h1>{user.accountName}</h1>
|
||||
<a href={$"./account_packages?name={Net.Http.UrlEncode(name)}"}>Packages</a>|<a href={$"./reserved_prefixes?name={Net.Http.UrlEncode(name)}"}>Reserved Prefixes</a>
|
||||
<a href={$"./packages?account={Net.Http.UrlEncode(name)}"}>Packages</a>|<a href={$"./reserved_prefixes?name={Net.Http.UrlEncode(name)}"}>Reserved Prefixes</a>
|
||||
|
||||
<if(active.active)>
|
||||
<true>
|
||||
@@ -94,6 +94,11 @@ func Pages.Account(ctx)
|
||||
</if>
|
||||
<input class="btn btn-primary" type="submit" value="Save">
|
||||
<input type="hidden" name="csrf" value={DB.CreateCSRF(ctx)}>
|
||||
<if(active.admin)>
|
||||
<true>
|
||||
<a class="btn btn-secondary" href="./admin">Admin</a>
|
||||
</true>
|
||||
</if>
|
||||
<a class="btn btn-danger" href="./logout">Logout</a>
|
||||
</form>
|
||||
|
||||
|
||||
235
Tesses.CrossLang.PackageServer/src/pages/admin.tcross
Normal file
235
Tesses.CrossLang.PackageServer/src/pages/admin.tcross
Normal file
@@ -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,<h1>Unsupported Operation</h1>);}
|
||||
if(!active.admin) {ctx.StatusCode = 401; return Shell("Not an admin", pages,<h1>Not an admin</h1>);}
|
||||
if(TypeOf(csrf2) != "String") {ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);}
|
||||
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,<h1>Invalid Input</h1>);
|
||||
}
|
||||
}
|
||||
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,<h1>Invalid Input</h1>);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ctx.StatusCode=501;
|
||||
return Shell($"Unsupported operation {action2}", pages,<h1>Unsupported operation {action2}</h1>);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var html = <div class="container"><if(active.admin)>
|
||||
<true>
|
||||
/*<if(ctx.Method == "POST")>
|
||||
<true>
|
||||
<ul>
|
||||
<each(var item : ctx.QueryParams.ToList())>
|
||||
<li><b>{item.Key}</b>: {item.Value}</li>
|
||||
</each>
|
||||
</ul>
|
||||
</true>
|
||||
</if>*/
|
||||
<fieldset class="border p-2">
|
||||
<legend class="float-none w-auto">Cache Package</legend>
|
||||
<form action="./admin" method="post">
|
||||
<input type="hidden" name="action" value="cache_package">
|
||||
<input type="hidden" name="csrf" value={csrf}>
|
||||
<input class="form-control" name="url" type="url" placeholder="Package Url" aria-label="Package Url">
|
||||
|
||||
<input type="submit" class="btn btn-primary" value="Add Package">
|
||||
</form>
|
||||
</fieldset>
|
||||
<fieldset class="border p-2">
|
||||
<legend class="float-none w-auto">Accounts</legend>
|
||||
<form action="./admin_account" method="get">
|
||||
<div class="mb-3">
|
||||
<label for="account" class="form-label">Account Name</label>
|
||||
<input class="form-control" name="account" id="account" type="text" placeholder="Account Name" aria-label="Account Name">
|
||||
</div>
|
||||
<input type="submit" class="btn btn-primary" value="Manage Account">
|
||||
</form>
|
||||
<a class="btn btn-primary" href="/admin_accounts">View Accounts</a>
|
||||
<a class="btn btn-primary" href="/admin_register">Register Account</a>
|
||||
</fieldset>
|
||||
<fieldset class="border p-2">
|
||||
<legend class="float-none w-auto">Server Configuration</legend>
|
||||
<form action="./admin" method="post">
|
||||
<input type="hidden" name="csrf" value={csrf}>
|
||||
<input type="hidden" name="action" value="server_config">
|
||||
<div class="mb-3">
|
||||
<label for="prefix" class="form-label">Prefix Url (url prefix in emails)</label>
|
||||
<input class="form-control" name="prefix" id="prefix" type="text" placeholder="Prefix Url" aria-label="Prefix Url (url prefix in emails)" value={TypeOf(DB.Config.Prefix) == "String" ? DB.Config.Prefix : ""}>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="port" class="form-label">Server Listening Port</label>
|
||||
<input class="form-control" name="port" id="port" type="number" placeholder="Port" aria-label="Server Listening Port" value={TypeOf(DB.Config.Port) == "Long" ? DB.Config.Port : 4206}>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
|
||||
<if(DB.Config.AllowRegister)>
|
||||
<true>
|
||||
<input class="form-check-input" type="checkbox" name="allowRegistering" id="allowRegistering" checked>
|
||||
</true>
|
||||
<false>
|
||||
<input class="form-check-input" type="checkbox" name="allowRegistering" id="allowRegistering">
|
||||
</false>
|
||||
</if>
|
||||
|
||||
//DB.Config.Prefix
|
||||
<label class="form-check-label" for="allowRegistering">
|
||||
Allow Registering
|
||||
</label>
|
||||
|
||||
|
||||
</div>
|
||||
<input type="submit" class="btn btn-primary" value="Save">
|
||||
</form>
|
||||
</fieldset>
|
||||
<fieldset class="border p-2">
|
||||
<legend class="float-none w-auto">SMTP Configuration</legend>
|
||||
<form action="./admin" method="post">
|
||||
<input type="hidden" name="csrf" value={csrf}>
|
||||
<input type="hidden" name="action" value="smtp_config">
|
||||
<div class="mb-3">
|
||||
<label for="host" class="form-label">Host</label>
|
||||
<input class="form-control" name="host" id="host" type="text" placeholder="Host" aria-label="Host" value={TypeOf(DB.Config.MailConfig.Server.host) == "String" ? DB.Config.MailConfig.Server.host : ""}>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="smtpport" class="form-label">Port (0 for default)</label>
|
||||
<input class="form-control" name="port" id="smtpport" type="number" placeholder="Port" aria-label="Port" value={TypeOf(DB.Config.MailConfig.Server.port) == "Long" ? DB.Config.MailConfig.Server.port : 0}>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<if(TypeOf(DB.Config.MailConfig.Server.tls) != "Boolean" || DB.Config.MailConfig.Server.tls)>
|
||||
<true>
|
||||
<input class="form-check-input" type="checkbox" name="tls" id="tls" checked>
|
||||
</true>
|
||||
<false>
|
||||
<input class="form-check-input" type="checkbox" name="tls" id="tls">
|
||||
</false>
|
||||
</if>
|
||||
|
||||
//DB.Config.Prefix
|
||||
<label class="form-check-label" for="tls">
|
||||
Enable TLS (you should definitely use this, unless you can{'\''}t in your setup)
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username (without domain)</label>
|
||||
<input class="form-control" name="username" id="username" type="text" placeholder="Username" aria-label="Username" value={TypeOf(DB.Config.MailConfig.Auth.username) == "String" ? DB.Config.MailConfig.Auth.username : ""}>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password (WARNING this password is stored plaintext)</label>
|
||||
<input class="form-control" name="password" id="password" type="password" placeholder="Password" aria-label="Password">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email (the email you want in From header)</label>
|
||||
<input class="form-control" name="email" id="email" type="email" placeholder="Email" aria-label="Email" value={TypeOf(DB.Config.MailConfig.From.email) == "String" ? DB.Config.MailConfig.From.email : ""}>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="fromname" class="form-label">Name (the name you want in From header)</label>
|
||||
<input class="form-control" name="fromname" id="fromname" type="text" placeholder="Name" aria-label="Name" value={TypeOf(DB.Config.MailConfig.From.name) == "String" ? DB.Config.MailConfig.From.name : ""}>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="domain" class="form-label">Email Domain (if email is user@example.com it will be example.com)</label>
|
||||
<input class="form-control" name="domain" id="domain" type="text" placeholder="Domain" aria-label="Domain" value={TypeOf(DB.Config.MailConfig.Domain) == "String" ? DB.Config.MailConfig.Domain : ""}>
|
||||
</div>
|
||||
<input type="submit" class="btn btn-primary" value="Save">
|
||||
</form>
|
||||
</fieldset>
|
||||
</true>
|
||||
<false>
|
||||
<h1>You are not authorized in the admin panel</h1>
|
||||
</false>
|
||||
</if></div>;
|
||||
|
||||
return Shell("Admin", pages,html);
|
||||
}
|
||||
@@ -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,<h1>Not an admin</h1>);}
|
||||
if(TypeOf(csrf2) != "String") {ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);}
|
||||
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,<h1>Invalid input</h1>);
|
||||
|
||||
if(password != confirm)
|
||||
return Shell("Passwords do not match",pages,<h1>Passwords do not match</h1>);
|
||||
|
||||
var res = DB.CreateUserFromAdmin(email, displayName, password, flags);
|
||||
|
||||
if(!res.Success)
|
||||
{
|
||||
return Shell(res.Reason, pages, <h1>{res.Reason}</h1>);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var html = <div class="container">
|
||||
<if(active.admin)>
|
||||
<true>
|
||||
<form method="POST" action="./admin_register">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input class="form-control" name="email" id="email" type="email" placeholder="Email" aria-label="Email">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="displayName" class="form-label">Display Name</label>
|
||||
<input class="form-control" name="displayName" id="displayName" type="text" placeholder="Display Name" aria-label="Display Name">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input class="form-control" name="password" id="password" type="password" placeholder="Password" aria-label="Password">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="confirm" class="form-label">Confirm Password</label>
|
||||
<input class="form-control" name="confirm" id="confirm" type="password" placeholder="Confirm Password" aria-label="Confirm Password">
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="admin" id="admin">
|
||||
<label class="form-check-label" for="admin">Administrator</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="verified" id="verified" checked>
|
||||
<label class="form-check-label" for="verified">Verified</label>
|
||||
</div>
|
||||
<input type="hidden" name="csrf" value={csrf}>
|
||||
<input type="submit" class="btn btn-primary" value="Register">
|
||||
</form>
|
||||
</true>
|
||||
<false>
|
||||
<h1>You are not authorized in the admin panel</h1>
|
||||
</false>
|
||||
</if>
|
||||
</div>;
|
||||
|
||||
return Shell("Admin Register", pages,html);
|
||||
}
|
||||
@@ -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,<h1>Reset Password Failed {reason}</h1>);
|
||||
}
|
||||
@@ -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 = <div class={"container min-vh-100 d-flex justify-content-center align-items-center"}>
|
||||
<form action={"./forgot_password"} method={"post"} enctype={"application/x-www-form-urlencoded"}>
|
||||
<div class={"mb-3"}>
|
||||
<label for={"email"} class={"form-label"}>Email address</label>
|
||||
<input type={"email"} class={"form-control"} id={"email"} name={"email"} aria-describedby={"emailHelp"}>
|
||||
<div id={"emailHelp"} class={"form-text"}>{"We'll"} never share your email with anyone else.</div>
|
||||
</div>
|
||||
<button type={"submit"} class={"btn btn-primary"}>Reset Password</button>
|
||||
</form>
|
||||
</div>;
|
||||
return Shell("Forgot Password",pages,html);
|
||||
}
|
||||
@@ -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 = <article><h1 class={"display-5"} style={"text-align: center;"}>Make crosslang development faster and more convenient with CPKG</h1><form action={"./packages"} method={"get"}><div class={"container text-center"}><div class={"row"}><div class={"col"}>
|
||||
|
||||
</div>
|
||||
@@ -23,7 +30,8 @@ func Pages.Index(ctx)
|
||||
<button class={"btn btn-outline-secondary"} type={"submit"} id={"button-search"}>Search</button>
|
||||
</div>
|
||||
|
||||
<a href="./api">API</a>
|
||||
<a href="./api">API</a><br>
|
||||
<a href={$"{repo}{commit}"}>Commit {commit.Remove(8)}</a>
|
||||
</div>
|
||||
<div class={"col"}>
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ func Pages.Login(ctx)
|
||||
<input type={"password"} class={"form-control"} name={"password"} id={"password"}>
|
||||
</div>
|
||||
<a href={"./signup"}>Sign up</a>
|
||||
<a href={"./forgot_password"}>Forgot Password</a>
|
||||
<button type={"submit"} class={"btn btn-primary"}>Login</button>
|
||||
</form>
|
||||
</div>;
|
||||
|
||||
38
Tesses.CrossLang.PackageServer/src/pages/new_password.tcross
Normal file
38
Tesses.CrossLang.PackageServer/src/pages/new_password.tcross
Normal file
@@ -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 = <div class={"container min-vh-100 d-flex justify-content-center align-items-center"}>
|
||||
<form action={"./new_password"} method={"post"} enctype={"application/x-www-form-urlencoded"}>
|
||||
|
||||
<input type="hidden" name="code" value={code}>
|
||||
|
||||
<div class={"mb-3"}>
|
||||
<label for={"password"} class={"form-label"}>Password</label>
|
||||
<input type={"password"} class={"form-control"} name={"password"} id={"password"}>
|
||||
</div>
|
||||
<div class={"mb-3"}>
|
||||
<label for={"confirm"} class={"form-label"}>Confirm Password</label>
|
||||
<input type={"password"} class={"form-control"} name={"confirm"} id={"confirm"}>
|
||||
</div>
|
||||
|
||||
<button type={"submit"} class={"btn btn-primary"}>Reset Password</button>
|
||||
</form>
|
||||
</div>;
|
||||
|
||||
return Shell("Reset your password", pages, html);
|
||||
}
|
||||
@@ -113,6 +113,7 @@ func Pages.Package(ctx,name)
|
||||
<li>Latest version: {package[0].version}</li>
|
||||
<li>Type: {package[0].type}</li>
|
||||
<li><a href={package[0].download}>Download</a></li>
|
||||
<li><a href={package[0].docs}>View Docs</a></li>
|
||||
</ul>
|
||||
<if(package.Length > 1)>
|
||||
<true>
|
||||
@@ -153,6 +154,8 @@ func Pages.Package(ctx,name)
|
||||
<li>Uploaded: {new DateTime(package[i].uploadTime).ToString("%Y/%m/%d %H:%M:%S UTC")}</li>
|
||||
<li>Type: {package[i].type}</li>
|
||||
<li><a href={package[i].download}>Download</a></li>
|
||||
|
||||
<li><a href={package[i].docs}>View Docs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
52
Tesses.CrossLang.PackageServer/src/pages/package_docs.tcross
Normal file
52
Tesses.CrossLang.PackageServer/src/pages/package_docs.tcross
Normal file
@@ -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 = <div class="container">
|
||||
|
||||
<h1>Functions</h1>
|
||||
<ul>
|
||||
<each(var fn : functions)>
|
||||
|
||||
<li><raw(Components.DocFunction(fn))></li>
|
||||
</each>
|
||||
</ul>
|
||||
|
||||
<h1>Classes</h1>
|
||||
<ul>
|
||||
<each(var cls : classes)>
|
||||
|
||||
<li><raw(Components.DocClass(cls))></li>
|
||||
</each>
|
||||
</ul>
|
||||
</div>;
|
||||
|
||||
return Shell("Documentation", pages, html);
|
||||
}
|
||||
@@ -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,<h1>Unsupported Operation</h1>);}
|
||||
if(!active.active) {ctx.StatusCode = 401; return Shell("Not an", pages,<h1>Not the correct user</h1>);}
|
||||
if(TypeOf(csrf2) != "String") {ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);}
|
||||
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, <h1>{res.Reason}</h1>);
|
||||
}
|
||||
}
|
||||
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, <h1>{res.Reason}</h1>);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var items = DB.QueryReservedPrefixes(name);
|
||||
|
||||
var html = <div class="container">
|
||||
<if(active.active)>
|
||||
<true>
|
||||
<form method="POST" action={$"/reserved_prefixes?name={Net.Http.UrlEncode(name)}"}>
|
||||
<input type="hidden" name="action" value="add">
|
||||
<input type="hidden" name="csrf" value={csrf}>
|
||||
<div class="mb-3">
|
||||
<label for="prefix" class="form-label">Prefix</label>
|
||||
<input type="text" class="form-control" id="prefix" name="prefix" placeholder="Prefix">
|
||||
</div>
|
||||
<input type="submit" class="btn btn-primary" value="Add">
|
||||
</form>
|
||||
</true>
|
||||
</if>
|
||||
<if(items.Success)>
|
||||
<true>
|
||||
<ul>
|
||||
<each(var item : items.Items)>
|
||||
<li>
|
||||
<raw(Components.Prefix(item,active.active,name,csrf))>
|
||||
</li>
|
||||
</each>
|
||||
</ul>
|
||||
</true>
|
||||
<false>
|
||||
<h1>Failed to get prefixes {items.Reason}</h1>
|
||||
</false>
|
||||
</if>
|
||||
</div>;
|
||||
|
||||
return Shell("Reserved Prefixes", pages, html);
|
||||
}
|
||||
@@ -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)
|
||||
<input type={"search"} name={"q"} class={"form-control"} placeholder={"Search for packages..."} aria-label={"Search for packages..."} aria-describedby={"button-search"} value={q}>
|
||||
|
||||
<button class={"btn btn-outline-secondary"} type={"submit"} id={"button-search"}>Search</button>
|
||||
<if(TypeOf(account) == "String")>
|
||||
<true>
|
||||
<input type="hidden" name="account" value={account}>
|
||||
</true>
|
||||
</if>
|
||||
</div>
|
||||
</div>
|
||||
<div class={"col"}>
|
||||
@@ -36,9 +56,33 @@ func Pages.Packages(ctx)
|
||||
</article>
|
||||
+
|
||||
<ul>
|
||||
<each(var item : DB.QueryPackages(q,(page-1)*20,20))>
|
||||
<each(var item : DB.QueryPackages(q,(page-1)*20,20,account))>
|
||||
<raw(Components.Package(item.packageName,item.version,item.accountName,new DateTime(item.uploadTime).ToString("%Y/%m/%d"),item.description))>
|
||||
</each>
|
||||
</ul>;
|
||||
</ul>
|
||||
+
|
||||
<nav aria-label="Page navigation example">
|
||||
<ul class="pagination justify-content-center">
|
||||
<if(page == 1)>
|
||||
<true>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link">Previous</a>
|
||||
</li>
|
||||
</true>
|
||||
<false>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href={$"{path}&page={page-1}"}>Previous</a>
|
||||
</li>
|
||||
</false>
|
||||
</if>
|
||||
<for(var i = 1; i <= 3; i++)>
|
||||
|
||||
<li class={i == cur + 1 ? "page-item active" : "page-item"}><a class="page-link" href={$"{path}&page={i+firstPage}"}>{i+firstPage}</a></li>
|
||||
</for>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href={$"{path}&page={page+1}"}>Next</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>;
|
||||
return Shell("Packages",pages,html);
|
||||
}
|
||||
@@ -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,<h1>Verification Failed {reason}</h1>);
|
||||
}
|
||||
@@ -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",[],<h1>Not logged in</h1>));
|
||||
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("<h1>You forgot the email buddy</h1>");
|
||||
return true;
|
||||
}
|
||||
var res = DB.ForgotPassword(email);
|
||||
if(!res.Success)
|
||||
{
|
||||
ctx.StatusCode = 400;
|
||||
ctx.SendText($"<h1>Error: {Net.Http.HtmlEncode(res.Reason)}</h1>");
|
||||
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(%"<h1>Error: {Net.Http.HtmlEncode(res.Reason)}</h1>");
|
||||
ctx.SendText($"<h1>Error: {Net.Http.HtmlEncode(res.Reason)}</h1>");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
8
Tesses.CrossLang.Reference/cross.json
Normal file
8
Tesses.CrossLang.Reference/cross.json
Normal file
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/^ Write Text To Standard Output ^/
|
||||
func Console.WriteLine(text)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/^ Get Documents Folder ^/
|
||||
func Env.getDocuments()
|
||||
{
|
||||
|
||||
}
|
||||
10
Tesses.CrossLang.Reference/src/runtime_methods/fs.tcross
Normal file
10
Tesses.CrossLang.Reference/src/runtime_methods/fs.tcross
Normal file
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
16
Tesses.CrossLang.Reference/src/runtime_methods/json.tcross
Normal file
16
Tesses.CrossLang.Reference/src/runtime_methods/json.tcross
Normal file
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
71
Tesses.CrossLang.Reference/src/types/list.tcross
Normal file
71
Tesses.CrossLang.Reference/src/types/list.tcross
Normal file
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
24
Tesses.CrossLang.Reference/src/types/mutex.tcross
Normal file
24
Tesses.CrossLang.Reference/src/types/mutex.tcross
Normal file
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
17
Tesses.CrossLang.Reference/src/types/object.tcross
Normal file
17
Tesses.CrossLang.Reference/src/types/object.tcross
Normal file
@@ -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 {
|
||||
|
||||
}
|
||||
74
Tesses.CrossLang.Reference/src/types/stream.tcross
Normal file
74
Tesses.CrossLang.Reference/src/types/stream.tcross
Normal file
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
18
Tesses.CrossLang.Reference/src/types/string.tcross
Normal file
18
Tesses.CrossLang.Reference/src/types/string.tcross
Normal file
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user