func main(args) { var dir = "."; if(args.Length > 1) { dir = args[1]; } DB.Init(dir); const timer = new Timer(()=>{ const time = DateTime.NowEpoch ?? 0; DB.Lock(); const db = DB.Open(); Sqlite.Exec(db, "DELETE FROM sessions WHERE expires < {time} AND expires > 0;"); Sqlite.Close(db); DB.Unlock(); },900000); /* PUT /api/v1/upload Authorization Bearer POST /api/v1/login Json object with email and password returns json object with either 200 for success {"token": "TOKEN_VAL"} or non 2XX if fails {"reason": "SOME ERROR"} POST /api/v1/logout use Authorization Bearer GET /api/v1/latest?name=PackageName returns 200 OK with json {"version":"1.0.0.0-dev"} if it succeeds if it fails returns a failing status code with {"reason": "SOME ERROR"} GET /api/v1/download?name=PackageName&version=1.0.0.0-prod returns 200 OK with package bytes or 404 if doesn't exist GET /api/v1/search?q=SomeQuery&offset=&limit= returns 200 OK with json of packages {"packages": [{"name": "pkgName","version": "latestVersion", ...}]} or non success status code */ 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 == "/admin_accounts") { ctx.WithMimeType("text/html").SendText(Pages.AdminAccount(ctx)); return true; } if(ctx.Path == "/package") { var name = ctx.QueryParams.TryGetFirst("name"); if(TypeOf(name) != "String") name = ""; 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)); return true; } if(ctx.Path == "/api/v1/latest") { var name = ctx.QueryParams.TryGetFirst("name"); if(TypeOf(name) != "String") name = ""; var version = DB.GetLatestVersion(name); if(version != null) { ctx.WithMimeType("application/json").SendText(Json.Encode({version})); return true; } } if(ctx.Path == "/api/v1/search") { var filter = ""; var type = ctx.QueryParams.TryGetFirst("type"); if(TypeOf(type) != "String") type = ""; var types = type.Length > 0 ? type.Split(",") : []; var pluginHost = ctx.QueryParams.TryGetFirst("pluginHost"); if(TypeOf(pluginHost) == "String") { filter += $" AND v.pluginHost = {Sqlite.Escape(pluginHost)}"; } if(types.Length > 0) { filter += " AND ("; var first = true; each(var item : types) { if(!first) { filter += " OR "; } filter += $"v.type = {Sqlite.Escape(item)}"; first=false; } filter += ")"; } var q = ctx.QueryParams.TryGetFirst("q"); if(TypeOf(q) != "String") q = ""; var offset = ParseLong(ctx.QueryParams.TryGetFirst("offset")); if(TypeOf(offset) != "Long") offset=0; var limit = ParseLong(ctx.QueryParams.TryGetFirst("limit")); if(TypeOf(limit) != "Long") limit = 20; if(limit <= 0) limit = 20; var res = DB.QueryPackages(q, offset*limit, limit, undefined, filter); if(TypeOf(res) != "List") { ctx.StatusCode=500; ctx.SendText("Packages not a list"); return true; } else { ctx.WithMimeType("application/json").SendText(Json.Encode({packages=res})); return true; } } if(ctx.Path == "/change_motto") { if(ctx.Method == "POST") { var motto = ctx.QueryParams.TryGetFirst("motto"); if(motto == null) motto = ""; var session = DB.GetSession(ctx); if(session == null) { ctx.StatusCode = 401; ctx.SendText("

You are not logged in

"); return true; } var csrf = ctx.QueryParams.TryGetFirst("csrf"); var result = { Success=false, Reason = "Invalid CSRF"}; if(DB.VerifyCSRF(session,csrf)) { var userId = DB.GetUserIdFromSession(session).accountId; var url = DB.ChangeMotto(userId,motto); ctx.StatusCode = 303; ctx.ResponseHeaders.SetValue("Location", url); ctx.WithMimeType("text/html").SendText(

Redirecting

Click here if it does not redirect
); return true; } } } if(ctx.Path == "/upload") { if(ctx.Method == "GET") { ctx.WithMimeType("text/html").SendText(Pages.Upload(ctx)); return true; } else if(ctx.Method == "POST") { if(ctx.NeedToParseFormData) { var filePath = DB.working / "Temp" / $"{DB.GetUniqueNumber()}.crvm"; var hasFile=false; var strm = FS.Local.OpenFile(filePath,"wb"); ctx.ParseFormData((mime,filename,name)=>{ if(name == "package") { if(hasFile) return new MemoryStream(true); hasFile=true; return strm; } else return new MemoryStream(true); }); strm.Close(); var session = DB.GetSession(ctx); if(session == null) { if(FS.Local.FileExists(filePath)) FS.Local.DeleteFile(filePath); ctx.StatusCode = 401; ctx.SendText("

You are not logged in

"); return true; } var csrf = ctx.QueryParams.TryGetFirst("csrf"); var result = { Success=false, Reason = "Invalid CSRF"}; if(!DB.VerifyCSRF(session,csrf)) { var userId = DB.GetUserIdFromSession(session).accountId; result = DB.UploadPackage(userId, filePath); } if(FS.Local.FileExists(filePath)) FS.Local.DeleteFile(filePath); if(result.Success) { ctx.StatusCode = 303; ctx.ResponseHeaders.SetValue("Location", "/"); ctx.WithMimeType("text/html").SendText(

Redirecting

Click here if it does not redirect
); } else { ctx.StatusCode = 400; ctx.SendText(result.Reason); } return true; } } } if(ctx.Path == "/api") { ctx.WithMimeType("text/html").SendText(Pages.API.Index()); return true; } if(ctx.Path == "/api-v1") { ctx.WithMimeType("text/html").SendText(Pages.API.V1()); return true; } if(ctx.Path == "/api/v1/versions") { var name = ctx.QueryParams.TryGetFirst("name"); if(TypeOf(name) == "String") { var versions = DB.GetPackageVersions(name); ctx.WithMimeType("application/json").SendJson({ success=true, versions }); return true; } ctx.WithMimeType("application/json").SendJson({ success=false }); return true; } if(ctx.Path == "/delete_packages") { if(ctx.Method == "GET") { ctx.WithMimeType("text/html").SendText(Pages.DeletePackages(ctx)); return true; } else if(ctx.Method == "POST") { var packages = ctx.QueryParams.TryGetFirst("packages"); var email = ctx.QueryParams.TryGetFirst("email"); var password = ctx.QueryParams.TryGetFirst("password"); var msg = DB.DeletePackages(email,password, packages); var html =

{msg}

; ctx.WithMimeType("text/html").SendText(Shell(msg,[], html)); } } if(ctx.Path == "/api/v1/upload") { if(ctx.Method == "PUT") { var session = DB.GetSessionFromBearer(ctx); if(session == null) { ctx.StatusCode=401; ctx.SendJson({ reason = "You are not logged in" }); return true; } var userId = DB.GetUserIdFromSession(session).accountId; var filePath = DB.working / "Temp" / $"{DB.GetUniqueNumber()}.crvm"; var strm = FS.Local.OpenFile(filePath,"wb"); ctx.ReadStream(strm); strm.Close(); var result = DB.UploadPackage(userId, filePath); if(result.Success) { ctx.StatusCode = 204; ctx.ResponseHeaders.SetValue("Content-Length","0"); ctx.WriteHeaders(); return true; } else { ctx.StatusCode = 400; ctx.SendJson({reason = result.Reason}); return true; } } else { ctx.StatusCode = 400; ctx.SendJson({ reason = $"Expected PUT method got {ctx.Method}" }); } return true; } if(ctx.Path == "/api/v1/login") { if(ctx.Method == "POST") { var json = ctx.ReadJson(); if(TypeOf(json) != "Dictionary" || TypeOf(json.email) != "String" || TypeOf(json.password) != "String") { ctx.StatusCode = 400; ctx.SendJson({ reason = "Expected a Json Dictionary, with the email and password" }); return true; } var accountId = DB.GetAccountId(json.email, json.password); if(accountId == -1) { ctx.StatusCode = 401; ctx.SendJson({ reason = "Invalid credentials" }); return true; } ctx.SendJson({ token = DB.CreateSession(accountId,false, TypeIsString(json.name) ? json.name : "CrossLang Shell") }); return true; } else { ctx.StatusCode = 400; ctx.SendJson({ reason = $"Expected POST method got {ctx.Method}" }); } return true; } if(ctx.Path == "/api/v1/package_icon.png") { var name = ctx.QueryParams.TryGetFirst("name"); var version = ctx.QueryParams.TryGetFirst("version"); ctx.ResponseHeaders.SetValue("Content-Type", "image/png"); ctx.SendBytes(DB.GetPackageIcon(name,version)); return true; } if(ctx.Path == "/api/v1/download") { var name = ctx.QueryParams.TryGetFirst("name"); var version = ctx.QueryParams.TryGetFirst("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) { ctx.WithMimeType("application/crvm").WithContentDisposition($"{name}-{version}.crvm",false).SendStream(strm); strm.Close(); return true; } } 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.WithMimeType("text/html").SendText(

Redirecting

Click here if it does not redirect
); return true; } else { ctx.WithMimeType("text/html").SendText(Shell("Not logged in",[],

Not logged in

)); return true; } } if(ctx.Path == "/login") { if(ctx.Method == "GET") { ctx.WithMimeType("text/html").SendText(Pages.Login(ctx)); return true; } else if(ctx.Method == "POST") { var email = ctx.QueryParams.TryGetFirst("email"); if(TypeOf(email) != "String") { ctx.StatusCode = 400; ctx.SendText("

You forgot the email buddy

"); return true; } var password = ctx.QueryParams.TryGetFirst("password"); if(TypeOf(password) != "String") { ctx.StatusCode = 400; ctx.SendText("

You forgot the password buddy

"); return true; } var accountId = DB.GetAccountId(email, password); if(accountId == -1) { ctx.StatusCode = 400; ctx.SendText(Shell("Invalid credentials",[],"

Invalid credentials

")); return true; } ctx.StatusCode = 303; ctx.ResponseHeaders.SetValue("Location", "/"); const browser="Browser"; const now = DateTime.NowEpoch??0; ctx.ResponseHeaders.SetValue("Set-Cookie", $"Session={DB.CreateSession(accountId,true, browser)}; SameSite=Strict; Expires={new DateTime(now+DB.Expires).ToHttpDate()}"); ctx.WithMimeType("text/html").SendText(

Redirecting

Click here if it does not redirect
); 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.WithMimeType("text/html").SendText(

Redirecting

Click here if it does not redirect
); 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 = 303; ctx.ResponseHeaders.SetValue("Location", "/"); ctx.WithMimeType("text/html").SendText(

Redirecting

Click here if it does not redirect
); return true; } else { ctx.StatusCode=401; ctx.WithMimeType("text/html").SendText(Pages.ResetPasswordFailed(res.Reason)); return true; } } else { ctx.StatusCode = 400; ctx.WithMimeType("text/html").SendText(Pages.ResetPasswordFailed("Invalid State")); return true; } } } if(ctx.Path == "/forgot_password") { if(ctx.Method == "GET") { ctx.WithMimeType("text/html").SendText(Pages.ForgotPassword(ctx)); return true; } else if(ctx.Method == "POST") { var email = ctx.QueryParams.TryGetFirst("email"); if(TypeOf(email) != "String") { ctx.StatusCode = 400; ctx.SendText("

You forgot the email buddy

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

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

"); return true; } ctx.StatusCode = 303; ctx.ResponseHeaders.SetValue("Location", res.Redirect); ctx.WithMimeType("text/html").SendText(

Redirecting

Click here if it does not redirect
); return true; } } if(ctx.Path == "/signup") { if(ctx.Method == "GET") { ctx.WithMimeType("text/html").SendText(Pages.Signup(ctx)); return true; } else if(ctx.Method == "POST") { var email = ctx.QueryParams.TryGetFirst("email"); if(TypeOf(email) != "String") { ctx.StatusCode = 400; ctx.SendText("

You forgot the email buddy

"); return true; } var displayName = ctx.QueryParams.TryGetFirst("displayName"); if(TypeOf(displayName) != "String") { ctx.StatusCode = 400; ctx.SendText("

You forgot the displayName buddy

"); return true; } var password = ctx.QueryParams.TryGetFirst("password"); if(TypeOf(password) != "String") { ctx.StatusCode = 400; ctx.SendText("

You forgot the password buddy

"); return true; } var passwordconfirm = ctx.QueryParams.TryGetFirst("passwordconfirm"); if(TypeOf(passwordconfirm) != "String") { ctx.StatusCode = 400; ctx.SendText("

You forgot the passwordconfirm buddy

"); return true; } if(password != passwordconfirm) { ctx.StatusCode = 400; ctx.SendText("

The passwords do not match

"); return true; } var res = DB.CreateUser(email, displayName, password); if(!res.Success) { ctx.StatusCode = 400; ctx.SendText(Shell("Error",[],$"

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

")); return true; } ctx.StatusCode = 303; ctx.ResponseHeaders.SetValue("Location", res.Redirect); ctx.WithMimeType("text/html").SendText(

Redirecting

Click here if it does not redirect
); return true; } } if(ctx.Path == "/account") { ctx.WithMimeType("text/html").SendText(Pages.Account(ctx)); return true; } if(ctx.Path == "/sessions") { ctx.WithMimeType("text/html").SendText(Pages.Sessions(ctx)); return true; } if(ctx.Path == "/create_session") { ctx.WithMimeType("text/html").SendText(Pages.CreateSession(ctx)); return true; } if(ctx.Path == "/session") { ctx.WithMimeType("text/html").SendText(Pages.Session(ctx)); return true; } if(ctx.Path == "/css/bootstrap.min.css") { ctx.WithMimeType("text/css").SendBytes(embed("css/bootstrap.min.css")); return true; } if(ctx.Path == "/css/bootstrap.min.css.map") { ctx.WithMimeType("application/json").SendBytes(embed("css/bootstrap.min.css.map")); return true; } if(ctx.Path == "/js/bootstrap.min.js") { ctx.WithMimeType("text/javascript").SendBytes(embed("js/bootstrap.min.js")); return true; } if(ctx.Path == "/js/bootstrap.min.js.map") { ctx.WithMimeType("application/json").SendBytes(embed("js/bootstrap.min.js.map")); return true; } if(ctx.Path == "/favicon.ico") { ctx.WithMimeType("image/x-icon").SendBytes(embed("favicon.ico")); return true; } if(ctx.Path == "/") { ctx.WithMimeType("text/html").SendText(Pages.Index(ctx)); return true; } return false; },DB.Port); timer.Callback = null; }