mirror of
https://onedev.site.tesses.net/crosslang/crosslangextras
synced 2026-02-08 09:05:46 +00:00
Get far on package manager
This commit is contained in:
2
Templates/emptyweb/.crossarchiveignore
Normal file
2
Templates/emptyweb/.crossarchiveignore
Normal file
@@ -0,0 +1,2 @@
|
||||
bin
|
||||
obj
|
||||
14
Templates/emptyweb/components/shell.tcrml
Normal file
14
Templates/emptyweb/components/shell.tcrml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?component(body) name="Shell" ?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Hello, world</title>
|
||||
</head>
|
||||
<body>
|
||||
<?body?>
|
||||
<?end?>
|
||||
</body>
|
||||
</html>
|
||||
<?end?>
|
||||
24
Templates/emptyweb/cross.json
Normal file
24
Templates/emptyweb/cross.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "Tesses.CrossLang.Template.EmptyWebsite",
|
||||
"version": "1.0.0.0-prod",
|
||||
"info": {
|
||||
"maintainer": "Mike Nolan",
|
||||
"type": "template",
|
||||
"repo": "https://onedev.site.tesses.net/CrossLang/CrossLangExtras",
|
||||
"homepage": "https://crosslang.tesseslanguage.com/",
|
||||
"license": "MIT",
|
||||
"template_name": "emptyweb",
|
||||
"description": "An empty website with my template engine",
|
||||
"template_info": {
|
||||
"type": "console"
|
||||
},
|
||||
"template_project_dependencies": [
|
||||
{
|
||||
"name": "Tesses.CrossLang.Markup",
|
||||
"version": "1.0.0.0-prod"
|
||||
}
|
||||
],
|
||||
"template_extra_text_ftles": [],
|
||||
"template_ignored_files": ["bin","obj"]
|
||||
}
|
||||
}
|
||||
8
Templates/emptyweb/pages/index.tcrml
Normal file
8
Templates/emptyweb/pages/index.tcrml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?page() route="/" ?>
|
||||
<?Shell?>
|
||||
<?arg_component() ?>
|
||||
<h1>Hello, world</h1>
|
||||
<span>Views: <?expr ++count ?></span>
|
||||
<?end?>
|
||||
<?end?>
|
||||
<?end?>
|
||||
5
Templates/emptyweb/src/program.tcross
Normal file
5
Templates/emptyweb/src/program.tcross
Normal file
@@ -0,0 +1,5 @@
|
||||
var count = 0;
|
||||
func main(args)
|
||||
{
|
||||
Net.Http.ListenSimpleWithLoop(Router,4206);
|
||||
}
|
||||
BIN
Tesses.CrossLang.PackageServer/res/crosslang.png
Normal file
BIN
Tesses.CrossLang.PackageServer/res/crosslang.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
6
Tesses.CrossLang.PackageServer/res/css/bootstrap.min.css
vendored
Normal file
6
Tesses.CrossLang.PackageServer/res/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
Tesses.CrossLang.PackageServer/res/js/bootstrap.min.js
vendored
Normal file
7
Tesses.CrossLang.PackageServer/res/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
621
Tesses.CrossLang.PackageServer/src/backend/db.tcross
Normal file
621
Tesses.CrossLang.PackageServer/src/backend/db.tcross
Normal file
@@ -0,0 +1,621 @@
|
||||
func DB.Open()
|
||||
{
|
||||
var path = DB.working / "data.db";
|
||||
return Sqlite.Open(path);
|
||||
}
|
||||
|
||||
/*func DB.GetPackageDetails(name)
|
||||
{
|
||||
DB.Lock();
|
||||
var dbCon = DB.Open();
|
||||
|
||||
Sqlite.Close(dbCon);
|
||||
DB.Unlock();
|
||||
}*/
|
||||
|
||||
func DB.CanUploadPackagePrefix(userId, packageName)
|
||||
{
|
||||
var prefix = packageName.Split(".",true,2);
|
||||
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 = {Time.Now} 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)},{Time.Now});");
|
||||
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.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);
|
||||
if(session)
|
||||
{
|
||||
var csrf = Crypto.Base64Encode(Crypto.RandomBytes(32, "CPKG"));
|
||||
|
||||
|
||||
var expires = Time.Now + 600;
|
||||
DB.Lock();
|
||||
DB.csrf.Add({
|
||||
Token = csrf,
|
||||
Expires = expires,
|
||||
Session = session
|
||||
});
|
||||
DB.Unlock();
|
||||
return csrf;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
func DB.VerifyCSRF(session,csrf)
|
||||
{
|
||||
if(TypeOf(csrf) != "String") return false;
|
||||
if(csrf.Length == 0) return false;
|
||||
csrf = csrf.Replace("\n","").Replace("\r","\n").Replace(" ","");
|
||||
if(csrf.Length == 0) return false;
|
||||
|
||||
var retVal = false;
|
||||
var time = Time.Now;
|
||||
DB.Lock();
|
||||
var _oldCSRF = DB.csrf;
|
||||
DB.csrf = [];
|
||||
//This will be inefficient but hopefully there won't be too many csrf tokens at any given time (we try to delete the expired ones)
|
||||
each(var csrfItem : _oldCSRF)
|
||||
{
|
||||
if(time >= csrfItem.Expires) continue;
|
||||
if(csrfItem.Token == csrf && time < csrfItem.Expires && session == csrfItem.Session)
|
||||
{
|
||||
retVal=true;
|
||||
continue;
|
||||
}
|
||||
DB.csrf.Add(csrfItem);
|
||||
}
|
||||
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()
|
||||
{
|
||||
DB.mtx.Lock();
|
||||
}
|
||||
|
||||
func DB.Unlock()
|
||||
{
|
||||
DB.mtx.Unlock();
|
||||
}
|
||||
|
||||
func DB.GetUniqueNumber()
|
||||
{
|
||||
DB.Lock();
|
||||
var unum = DB.u_n_counter++;
|
||||
DB.Unlock();
|
||||
return unum;
|
||||
}
|
||||
|
||||
func DB.Init(working)
|
||||
{
|
||||
DB.working = Path.FromString(working);
|
||||
DB.csrf = [];
|
||||
DB.u_n_counter=0;
|
||||
FS.Local.CreateDirectory(DB.working);
|
||||
FS.Local.CreateDirectory(DB.working / "Temp");
|
||||
FS.Local.CreateDirectory(DB.working / "Packages");
|
||||
|
||||
each(var f : FS.Local.EnumeratePaths(DB.working / "Temp"))
|
||||
{
|
||||
if(FS.Local.FileExists(f)) FS.Local.DeleteFile(f);
|
||||
}
|
||||
|
||||
DB.mtx = Mutex();
|
||||
var p = DB.working / "conf.json";
|
||||
if(FS.Local.FileExists(p))
|
||||
DB.Config = Json.Decode(FS.ReadAllText(FS.Local, p));
|
||||
else
|
||||
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);");
|
||||
Sqlite.Exec(dbCon,"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, uploadTime INTEGER);");
|
||||
Sqlite.Exec(dbCon,"CREATE TABLE IF NOT EXISTS accounts (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT UNIQUE, accountName TEXT UNIQUE, password_hash TEXT, password_salt TEXT, motto TEXT, verifyKey TEXT UNIQUE, flags INTEGER);");
|
||||
Sqlite.Exec(dbCon,"CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, accountId INTEGER, key STRING UNIQUE);");
|
||||
Sqlite.Exec(dbCon,"CREATE TABLE IF NOT EXISTS reserved_prefixes (id INTEGER PRIMARY KEY AUTOINCREMENT, accountId INTEGER, prefix STRING UNIQUE);");
|
||||
Sqlite.Close(dbCon);
|
||||
}
|
||||
|
||||
DB.FLAG_ADMIN = 0b00000010;
|
||||
DB.FLAG_VERIFIED = 0b00000001;
|
||||
|
||||
DB.FLAG_RESETPASS = 0b00001000;
|
||||
DB.FLAG_VERIFY = 0b00000100;
|
||||
|
||||
DB.ITTR = 35000;
|
||||
|
||||
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;
|
||||
}
|
||||
13
Tesses.CrossLang.PackageServer/src/components/package.tcross
Normal file
13
Tesses.CrossLang.PackageServer/src/components/package.tcross
Normal file
@@ -0,0 +1,13 @@
|
||||
func Components.Package(name,version,owner,upload,desc)
|
||||
{
|
||||
|
||||
var image = $"./api/v1/package_icon.png?name={Net.Http.UrlEncode(name)}&version={Net.Http.UrlEncode(version)}";
|
||||
var pkgUrl = $"./package?name={Net.Http.UrlEncode(name)}";
|
||||
return <li>
|
||||
<img width={"64"} height={"64"} src={image} alt={"icon"}>
|
||||
<a href={pkgUrl}>{name}</a> | <span>By: {owner}</span>
|
||||
<br>
|
||||
<span>Updated: {upload} | Latest version: {version}</span>
|
||||
<p>{desc}</p>
|
||||
</li>;
|
||||
}
|
||||
43
Tesses.CrossLang.PackageServer/src/components/shell.tcross
Normal file
43
Tesses.CrossLang.PackageServer/src/components/shell.tcross
Normal file
@@ -0,0 +1,43 @@
|
||||
func Shell(title,pages,body)
|
||||
{
|
||||
var shell=
|
||||
<html lang={"en"}>
|
||||
<head>
|
||||
<meta charset={"UTF-8"}>
|
||||
<meta name={"viewport"} content={"width=device-width, initial-scale=1.0"}>
|
||||
<link rel={"stylesheet"} href={"./css/bootstrap.min.css"}>
|
||||
<title>CPKG - {title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class={"navbar navbar-expand-lg"} style={"background-color: #ffff00;"}>
|
||||
<div class={"container-fluid"}>
|
||||
<a class={"navbar-brand"} href={"./"}>CPKG</a>
|
||||
<button class={"navbar-toggler"} type={"button"} data-bs-toggle={"collapse"} data-bs-target={"#navbarNav"} aria-controls={"navbarNav"} aria-expanded={"false"} aria-label={"Toggle navigation"}>
|
||||
<span class={"navbar-toggler-icon"}></span>
|
||||
</button>
|
||||
<div class={"collapse navbar-collapse"} id={"navbarNav"}>
|
||||
<ul class={"navbar-nav"}>
|
||||
<each(var item : pages)>
|
||||
<li class={"nav-item"}>
|
||||
<if(item.active)>
|
||||
<a class={"nav-link active"} aria-current={"page"} href={item.route}>{item.text}</a>
|
||||
<else>
|
||||
<a class={"nav-link"} href={item.route}>{item.text}</a>
|
||||
</if>
|
||||
</li>
|
||||
</each>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<br>
|
||||
|
||||
<raw(body)>
|
||||
<script src={"./js/bootstrap.min.js"}></script>
|
||||
</body>
|
||||
</html>;
|
||||
|
||||
return shell;
|
||||
}
|
||||
27
Tesses.CrossLang.PackageServer/src/pages/check_email.tcross
Normal file
27
Tesses.CrossLang.PackageServer/src/pages/check_email.tcross
Normal file
@@ -0,0 +1,27 @@
|
||||
func Pages.CheckEmail()
|
||||
{
|
||||
var pages = [
|
||||
{
|
||||
active = false,
|
||||
route = "/packages",
|
||||
text = "Packages"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/upload",
|
||||
text = "Upload"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/login",
|
||||
text = "Login"
|
||||
}
|
||||
];
|
||||
var html = <div class={"container"}>
|
||||
<h1>Please check your email.</h1>
|
||||
<p>
|
||||
The email may or may not be in your spam.
|
||||
</p>
|
||||
</div>;
|
||||
return Shell("Check your email",pages,html);
|
||||
}
|
||||
38
Tesses.CrossLang.PackageServer/src/pages/index.tcross
Normal file
38
Tesses.CrossLang.PackageServer/src/pages/index.tcross
Normal file
@@ -0,0 +1,38 @@
|
||||
func Pages.Index()
|
||||
{
|
||||
|
||||
var pages = [
|
||||
{
|
||||
active = false,
|
||||
route = "/packages",
|
||||
text = "Packages"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/upload",
|
||||
text = "Upload"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/login",
|
||||
text = "Login"
|
||||
}
|
||||
];
|
||||
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>
|
||||
<div class={"col-6"}>
|
||||
<div class={"input-group mb-3"}>
|
||||
<input type={"search"} name={"q"} class={"form-control"} placeholder={"Search for packages..."} aria-label={"Search for packages..."} aria-describedby={"button-search"}>
|
||||
<button class={"btn btn-outline-secondary"} type={"submit"} id={"button-search"}>Search</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class={"col"}>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</article>;
|
||||
return Shell("Main Page",pages,html);
|
||||
}
|
||||
36
Tesses.CrossLang.PackageServer/src/pages/login.tcross
Normal file
36
Tesses.CrossLang.PackageServer/src/pages/login.tcross
Normal file
@@ -0,0 +1,36 @@
|
||||
func Pages.Login()
|
||||
{
|
||||
var pages = [
|
||||
{
|
||||
active = false,
|
||||
route = "/packages",
|
||||
text = "Packages"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/upload",
|
||||
text = "Upload"
|
||||
},
|
||||
{
|
||||
active = true,
|
||||
route = "/login",
|
||||
text = "Login"
|
||||
}
|
||||
];
|
||||
var html = <div class={"container min-vh-100 d-flex justify-content-center align-items-center"}>
|
||||
<form action={"./login"} 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>
|
||||
<div class={"mb-3"}>
|
||||
<label for={"password"} class={"form-label"}>Password</label>
|
||||
<input type={"password"} class={"form-control"} name={"password"} id={"password"}>
|
||||
</div>
|
||||
<a href={"./signup"}>Sign up</a>
|
||||
<button type={"submit"} class={"btn btn-primary"}>Login</button>
|
||||
</form>
|
||||
</div>;
|
||||
return Shell("Login",pages,html);
|
||||
}
|
||||
157
Tesses.CrossLang.PackageServer/src/pages/package.tcross
Normal file
157
Tesses.CrossLang.PackageServer/src/pages/package.tcross
Normal file
@@ -0,0 +1,157 @@
|
||||
func Pages.Package(name)
|
||||
{
|
||||
var package = DB.GetPackageVersions(name);
|
||||
|
||||
var pages = [
|
||||
{
|
||||
active = false,
|
||||
route = "/packages",
|
||||
text = "Packages"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/upload",
|
||||
text = "Upload"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/login",
|
||||
text = "Login"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
|
||||
var html = <if(package.Length > 0)><section class={"container"}>
|
||||
<br>
|
||||
<img width={"64"} height={"64"} src={$"./api/v1/package_icon.png?name={Net.Http.UrlEncode(name)}&version={Net.Http.UrlEncode(package[0].version)}"} alt={"icon"}>
|
||||
<h1>{name}</h1>
|
||||
</section>
|
||||
<section class={"container"}>
|
||||
<h2>To install</h2>
|
||||
<ul class={"nav nav-tabs"} id={"myTab"} role={"tablist"}>
|
||||
<li class={"nav-item"} role={"presentation"}>
|
||||
<button class={"nav-link active"} id={"home-tab"} data-bs-toggle={"tab"} data-bs-target={"#home-tab-pane"} type={"button"} role={"tab"} aria-controls={"home-tab-pane"} aria-selected={"true"}>CLI</button>
|
||||
</li>
|
||||
<li class={"nav-item"} role={"presentation"}>
|
||||
<button class={"nav-link"} id={"profile-tab"} data-bs-toggle={"tab"} data-bs-target={"#profile-tab-pane"} type={"button"} role={"tab"} aria-controls={"profile-tab-pane"} aria-selected={"false"}>cross.json</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class={"tab-content"} id={"myTabContent"}>
|
||||
<div class={"tab-pane fade show active"} id={"home-tab-pane"} role={"tabpanel"} aria-labelledby={"home-tab"} tabindex={"0"}><div class={"card"}>
|
||||
|
||||
<div class={"card-body"}>
|
||||
<if(package[0].type == "lib" || package[0].type == "compile_tool")>
|
||||
crosslang add-dependency {name}
|
||||
</if>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class={"tab-pane fade"} id={"profile-tab-pane"} role={"tabpanel"} aria-labelledby={"profile-tab"} tabindex={"0"}><div class={"card"}>
|
||||
|
||||
<div class={"card-body"}>
|
||||
{Json.Encode({name=name,version = package[0].version })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<h3>More options and info</h3>
|
||||
<ul>
|
||||
<li>Account: <a href={$"./account?name={Net.Http.UrlEncode(package[0].accountName)}"}>{package[0].accountName}</a></li>
|
||||
<if(package[0].maintainer.Length > 0)>
|
||||
<li>Maintainer: {package[0].maintainer}</li>
|
||||
</if>
|
||||
<if(package[0].license.Length > 0)>
|
||||
<li>License: {package[0].license}</li>
|
||||
</if>
|
||||
<if(package[0].homepage.Length > 0)>
|
||||
<li>Homepage: <a href={package[0].homepage}>{package[0].homepage}</a></li>
|
||||
</if>
|
||||
<if(package[0].repo.Length > 0)>
|
||||
<li>Repo: <a href={package[0].repo}>{package[0].repo}</a></li>
|
||||
</if>
|
||||
<li>Last updated: {package[0].uploadDate}</li>
|
||||
<li>Latest version: {package[0].version}</li>
|
||||
<li>Type: {package[0].type}</li>
|
||||
<li><a href={package[0].download}>Download</a></li>
|
||||
</ul>
|
||||
<if(package.Length > 1)>
|
||||
<br>
|
||||
|
||||
<h3>Older versions</h3>
|
||||
<div class={"accordion accordion-flush"} id={"accordionFlushExample"}>
|
||||
<for(var i=1; i<package.Length; i++)>
|
||||
<div class={"accordion-item"}>
|
||||
<h2 class={"accordion-header"}>
|
||||
<button class={"accordion-button collapsed"} type={"button"} data-bs-toggle={"collapse"} data-bs-target={$"#collapse-{Net.Http.HtmlEncode(package[i].version)}"} aria-expanded={"false"} aria-controls={"flush-collapseOne"}>
|
||||
{package[i].version}
|
||||
</button>
|
||||
</h2>
|
||||
<div id={$"collapse-{Net.Http.HtmlEncode(package[i].version)}"} class={"accordion-collapse collapse"} data-bs-parent={"#accordionFlushExample"}>
|
||||
<div class={"accordion-body"}>
|
||||
<ul>
|
||||
<if(package[i].maintainer.Length > 0)>
|
||||
<li>Maintainer: {package[i].maintainer}</li>
|
||||
</if>
|
||||
<if(package[i].license.Length > 0)>
|
||||
<li>License: {package[i].license}</li>
|
||||
</if>
|
||||
<if(package[i].homepage.Length > 0)>
|
||||
<li>Homepage: <a href={package[i].homepage}>{package[i].homepage}</a></li>
|
||||
</if>
|
||||
<if(package[i].repo.Length > 0)>
|
||||
<li>Repo: <a href={package[i].repo}>{package[i].repo}</a></li>
|
||||
</if>
|
||||
<li>Uploaded: {package[i].uploadDate}</li>
|
||||
<li>Type: {package[i].type}</li>
|
||||
<li><a href={package[i].download}>Download</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</for>
|
||||
</div>
|
||||
</if>
|
||||
</section>
|
||||
</if>;
|
||||
return Shell($"Package {name}", pages,html);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
<section class="container">
|
||||
<br>
|
||||
<img width="64" height="64" src="https://api.nuget.org/v3-flatcontainer/newtonsoft.json/13.0.3/icon" alt="icon">
|
||||
<h1>Package</h1>
|
||||
</section>
|
||||
<section class="container">
|
||||
<h2>To install:</h2>
|
||||
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">CLI</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#profile-tab-pane" type="button" role="tab" aria-controls="profile-tab-pane" aria-selected="false">cross.json</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<div class="tab-pane fade show active" id="home-tab-pane" role="tabpanel" aria-labelledby="home-tab" tabindex="0"><div class="card">
|
||||
|
||||
<div class="card-body">
|
||||
crosslang add Package
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="profile-tab-pane" role="tabpanel" aria-labelledby="profile-tab" tabindex="0"><div class="card">
|
||||
|
||||
<div class="card-body">
|
||||
{"name": "Package","Version": "1.0.0.0-prod"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
*/
|
||||
46
Tesses.CrossLang.PackageServer/src/pages/search.tcross
Normal file
46
Tesses.CrossLang.PackageServer/src/pages/search.tcross
Normal file
@@ -0,0 +1,46 @@
|
||||
func Pages.Packages(ctx)
|
||||
{
|
||||
var q = ctx.QueryParams.TryGetFirst("q");
|
||||
if(TypeOf(q) != "String") q = "";
|
||||
var pages = [
|
||||
{
|
||||
active = true,
|
||||
route = "/packages",
|
||||
text = "Packages"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/upload",
|
||||
text = "Upload"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/login",
|
||||
text = "Login"
|
||||
}
|
||||
];
|
||||
var html = <article><form action={"./packages"} method={"get"}><div class={"container text-center"}><div class={"row"}><div class={"col"}>
|
||||
|
||||
</div>
|
||||
<div class={"col-6"}>
|
||||
<div class={"input-group mb-3"}>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div class={"col"}>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
+
|
||||
<ul>
|
||||
<each(var item : DB.QueryPackages(q,0,20))>
|
||||
<raw(Components.Package(item.packageName,item.version,item.accountName,item.uploadDate,item.description))>
|
||||
</each>
|
||||
</ul>;
|
||||
return Shell("Packages",pages,html);
|
||||
}
|
||||
44
Tesses.CrossLang.PackageServer/src/pages/signup.tcross
Normal file
44
Tesses.CrossLang.PackageServer/src/pages/signup.tcross
Normal file
@@ -0,0 +1,44 @@
|
||||
func Pages.Signup()
|
||||
{
|
||||
var pages = [
|
||||
{
|
||||
active = false,
|
||||
route = "/packages",
|
||||
text = "Packages"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/upload",
|
||||
text = "Upload"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/login",
|
||||
text = "Login"
|
||||
}
|
||||
];
|
||||
var html = <div class={"container min-vh-100 d-flex justify-content-center align-items-center"}>
|
||||
<form action={"./signup"} 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"} aria-describedby={"emailHelp"} name={"email"}>
|
||||
<div id={"emailHelp"} class={"form-text"}>{"We'll"} never share your email with anyone else.</div>
|
||||
</div>
|
||||
<div class={"mb-3"}>
|
||||
<label for={"displayName"} class={"form-label"}>Display Name</label>
|
||||
<input type={"text"} class={"form-control"} id={"displayName"} name={"displayName"}>
|
||||
</div>
|
||||
<div class={"mb-3"}>
|
||||
<label for={"password"} class={"form-label"}>Password</label>
|
||||
<input type={"password"} class={"form-control"} id={"password"} name={"password"}>
|
||||
</div>
|
||||
<div class={"mb-3"}>
|
||||
<label for={"passwordconfirm"} class={"form-label"}>Confirm Password</label>
|
||||
<input type={"password"} class={"form-control"} id={"passwordconfirm"} name={"passwordconfirm"}>
|
||||
</div>
|
||||
<a href={"./login"}>Login</a>
|
||||
<button type={"submit"} class={"btn btn-primary"}>Sign Up</button>
|
||||
</form>
|
||||
</div>;
|
||||
return Shell("Signup", pages, html);
|
||||
}
|
||||
29
Tesses.CrossLang.PackageServer/src/pages/upload.tcross
Normal file
29
Tesses.CrossLang.PackageServer/src/pages/upload.tcross
Normal file
@@ -0,0 +1,29 @@
|
||||
func Pages.Upload(ctx)
|
||||
{
|
||||
var pages = [
|
||||
{
|
||||
active = false,
|
||||
route = "/packages",
|
||||
text = "Packages"
|
||||
},
|
||||
{
|
||||
active = true,
|
||||
route = "/upload",
|
||||
text = "Upload"
|
||||
},
|
||||
{
|
||||
active = false,
|
||||
route = "/login",
|
||||
text = "Login"
|
||||
}
|
||||
];
|
||||
var html = <div class={"container min-vh-100 d-flex justify-content-center align-items-center"}>
|
||||
<form action={"./upload"} method={"post"} enctype={"multipart/form-data"}>
|
||||
<h1>Upload Package</h1>
|
||||
<input type={"file"} class={"form-control"} name={"package"} accept={".crvm"}>
|
||||
<button type={"submit"} class={"btn btn-primary"}>Upload</button>
|
||||
<input type={"hidden"} name={"csrf"} value={DB.CreateCSRF(ctx)}>
|
||||
</form>
|
||||
</div>;
|
||||
return Shell("Upload Package",pages,html);
|
||||
}
|
||||
54
build.tcross
Normal file
54
build.tcross
Normal file
@@ -0,0 +1,54 @@
|
||||
func cmd(name, args)
|
||||
{
|
||||
var filename = Env.GetRealExecutablePath(name).ToString();
|
||||
Console.Write($"Running command, {filename}");
|
||||
each(var arg : args)
|
||||
Console.Write($" {arg}");
|
||||
Console.WriteLine();
|
||||
var p= Process.Start({
|
||||
FileName = filename,
|
||||
Arguments = args
|
||||
});
|
||||
p.Join();
|
||||
}
|
||||
func main(args)
|
||||
{
|
||||
|
||||
var args2=["-o","./Tesses.CrossLang.BuildEssentials/bin-tmp","-n","Tesses.CrossLang.BuildEssentials","-v","1.0.0.0-prod","Tesses.CrossLang.BuildEssentials/main.tcross"];
|
||||
each(var f : FS.Local.EnumeratePaths("./Tesses.CrossLang.BuildEssentials/src"))
|
||||
{
|
||||
if(f.GetExtension() == ".tcross")
|
||||
{
|
||||
args2.Add(f.ToString());
|
||||
}
|
||||
}
|
||||
cmd("crossc", args2);
|
||||
cmd("crossvm",["./Tesses.CrossLang.BuildEssentials/bin-tmp/Tesses.CrossLang.BuildEssentials-1.0.0.0-prod.crvm","Tesses.CrossLang.Shell"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Tesses.CrossLang.PackageServer"]);
|
||||
//cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Tesses.CrossLang.WebSite"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Templates/console"]);
|
||||
// cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Templates/emptyweb"]);
|
||||
// cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Templates/web"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Templates/template"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Templates/lib"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Templates/compiletool"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Templates/tool"]);
|
||||
/*cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Tesses.CrossLang.PackageServer"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Tesses.CrossLang.PackageServer"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Tesses.CrossLang.PackageServer"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Tesses.CrossLang.PackageServer"]);
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","Tesses.CrossLang.PackageServer"]);*/
|
||||
cmd("crossvm",["./Tesses.CrossLang.Shell/bin/Tesses.CrossLang.Shell-1.0.0.0-prod.crvm","build","crosslang_shell_archive_maker"]);
|
||||
|
||||
if(args.Length > 1)
|
||||
{
|
||||
if(args[1] == "install")
|
||||
{
|
||||
cmd("crossvm",["crosslang_shell_archive_maker/bin/crosslang_shell_archive_maker-1.0.0.0-prod.crvm", "install"]);
|
||||
}
|
||||
else if(args[1] == "pack")
|
||||
{
|
||||
cmd("crossvm",["crosslang_shell_archive_maker/bin/crosslang_shell_archive_maker-1.0.0.0-prod.crvm"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user