From 58548a6576601533caa3a02cf73f1832d14c7e8b Mon Sep 17 00:00:00 2001 From: KyMAN <0kyman0@gmail.com> Date: Sun, 29 Mar 2026 20:37:07 +0200 Subject: [PATCH] #wip(py,sql,cs,js): Starting project. --- .gitignore | 12 + AnPv2.sln.old | 29 + CSharp/AnP.csproj | 23 + CSharp/AnP.slnx | 3 + CSharp/Application/AnP.cs | 9 + CSharp/Interfaces/Application/AnPInterface.cs | 5 + .../Managers/I18NManagerInterface.cs | 14 + CSharp/Managers/I18NManager.cs | 58 ++ CSharp/Managers/SettingsManager.cs | 31 + CSharp/Program.cs | 9 + CSharp/Types/ColorType.cs | 14 + CSharp/Types/I18NSentenceType.cs | 10 + CSharp/Utils/Check.cs | 41 ++ CSharp/Utils/Color.cs | 140 ++++ CSharp/Utils/Common.cs | 99 +++ CSharp/Utils/Options.cs | 57 ++ CSharp/Utils/Patterns.cs | 7 + DotNET/Dockerfile | 5 + DotNET/docker.rebuild.sh | 4 + DotNET/scripts/entrypoint.sh | 7 + JSON/AnP.settings.json | 69 ++ JSON/I18N/AnP.i18n.espanol.json | 44 ++ Public/ecma/Abstracts/BaseAbstract.ecma.js | 190 +++++ Public/ecma/Application/AnP.ecma.js | 67 ++ Public/ecma/Application/Components.ecma.js | 412 +++++++++++ Public/ecma/Drivers/FilesDriver.ecma.js | 155 ++++ Public/ecma/Managers/I18NManager.ecma.js | 139 ++++ .../ecma/Managers/IdentifiersManager.ecma.js | 89 +++ Public/ecma/Managers/SettingsManager.ecma.js | 135 ++++ Public/ecma/Utils/Check.ecma.js | 102 +++ Public/ecma/Utils/Options.ecma.js | 113 +++ Public/ecma/Utils/Patterns.ecma.js | 24 + Public/ecma/Utils/Utils.ecma.js | 404 ++++++++++ Public/json/AnP.settings.json | 1 + Public/json/i18n/anP.i18n.espanol.json | 3 + Python/Abstracts/BaseAbstract.py | 44 ++ Python/Application/AnP.py | 33 + Python/Interfaces/Application/AnPInterface.py | 31 + .../Managers/I18NManagerInterface.py | 18 + .../Managers/PrintTypesManagerInterface.py | 13 + .../Managers/SettingsManagerInterface.py | 18 + Python/Managers/I18NManager.py | 72 ++ Python/Managers/PrintTypesManager.py | 26 + Python/Managers/SettingsManager.py | 36 + Python/Models/PrintTypeModel.py | 30 + Python/Utils/Check.py | 27 + Python/Utils/Color.py | 75 ++ Python/Utils/Options.py | 60 ++ Python/Utils/Patterns.py | 8 + Python/Utils/Utils.py | 140 ++++ README.md | 38 +- SQLServer/AnP.1.10.common.server.sql | 690 ++++++++++++++++++ SQLServer/AnP.1.11.groups.server.sql | 345 +++++++++ SQLServer/Dockerfile | 4 + SQLServer/docker.rebuild.sh | 4 + Tools/reinstall.dockers.sh | 6 + Tools/run.cs.server.sh | 4 + Tools/run.server.sh | 3 + Tools/sass.sh | 3 + docker-compose.yml | 41 ++ version | 1 + 61 files changed, 4293 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 AnPv2.sln.old create mode 100755 CSharp/AnP.csproj create mode 100755 CSharp/AnP.slnx create mode 100755 CSharp/Application/AnP.cs create mode 100644 CSharp/Interfaces/Application/AnPInterface.cs create mode 100644 CSharp/Interfaces/Managers/I18NManagerInterface.cs create mode 100644 CSharp/Managers/I18NManager.cs create mode 100644 CSharp/Managers/SettingsManager.cs create mode 100755 CSharp/Program.cs create mode 100644 CSharp/Types/ColorType.cs create mode 100644 CSharp/Types/I18NSentenceType.cs create mode 100755 CSharp/Utils/Check.cs create mode 100755 CSharp/Utils/Color.cs create mode 100755 CSharp/Utils/Common.cs create mode 100755 CSharp/Utils/Options.cs create mode 100755 CSharp/Utils/Patterns.cs create mode 100644 DotNET/Dockerfile create mode 100755 DotNET/docker.rebuild.sh create mode 100755 DotNET/scripts/entrypoint.sh create mode 100644 JSON/AnP.settings.json create mode 100644 JSON/I18N/AnP.i18n.espanol.json create mode 100644 Public/ecma/Abstracts/BaseAbstract.ecma.js create mode 100644 Public/ecma/Application/AnP.ecma.js create mode 100644 Public/ecma/Application/Components.ecma.js create mode 100644 Public/ecma/Drivers/FilesDriver.ecma.js create mode 100644 Public/ecma/Managers/I18NManager.ecma.js create mode 100644 Public/ecma/Managers/IdentifiersManager.ecma.js create mode 100644 Public/ecma/Managers/SettingsManager.ecma.js create mode 100644 Public/ecma/Utils/Check.ecma.js create mode 100644 Public/ecma/Utils/Options.ecma.js create mode 100644 Public/ecma/Utils/Patterns.ecma.js create mode 100644 Public/ecma/Utils/Utils.ecma.js create mode 100644 Public/json/AnP.settings.json create mode 100644 Public/json/i18n/anP.i18n.espanol.json create mode 100644 Python/Abstracts/BaseAbstract.py create mode 100644 Python/Application/AnP.py create mode 100644 Python/Interfaces/Application/AnPInterface.py create mode 100644 Python/Interfaces/Managers/I18NManagerInterface.py create mode 100644 Python/Interfaces/Managers/PrintTypesManagerInterface.py create mode 100644 Python/Interfaces/Managers/SettingsManagerInterface.py create mode 100644 Python/Managers/I18NManager.py create mode 100644 Python/Managers/PrintTypesManager.py create mode 100644 Python/Managers/SettingsManager.py create mode 100644 Python/Models/PrintTypeModel.py create mode 100644 Python/Utils/Check.py create mode 100644 Python/Utils/Color.py create mode 100644 Python/Utils/Options.py create mode 100644 Python/Utils/Patterns.py create mode 100644 Python/Utils/Utils.py create mode 100644 SQLServer/AnP.1.10.common.server.sql create mode 100644 SQLServer/AnP.1.11.groups.server.sql create mode 100644 SQLServer/Dockerfile create mode 100755 SQLServer/docker.rebuild.sh create mode 100755 Tools/reinstall.dockers.sh create mode 100755 Tools/run.cs.server.sh create mode 100755 Tools/run.server.sh create mode 100755 Tools/sass.sh create mode 100644 docker-compose.yml create mode 100644 version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d16d594 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/Data +/Public/data +__pycache__ +.sass-cache +*.[Ss]ecret.* +*.[Ss]ecrets.* +*.deleted.* +/CSharp/bin +/CSharp/obj +/SQLServer/data +/SQLServer/scripts +/SQLServer/temporary \ No newline at end of file diff --git a/AnPv2.sln.old b/AnPv2.sln.old new file mode 100644 index 0000000..38626be --- /dev/null +++ b/AnPv2.sln.old @@ -0,0 +1,29 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CSharp", "CSharp", "{B41BF331-FCCB-2ADF-CDB6-767964B34647}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnP", "CSharp\AnP.csproj", "{720E15E3-2F0D-4B91-98F1-400300826A0A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {720E15E3-2F0D-4B91-98F1-400300826A0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {720E15E3-2F0D-4B91-98F1-400300826A0A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {720E15E3-2F0D-4B91-98F1-400300826A0A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {720E15E3-2F0D-4B91-98F1-400300826A0A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {720E15E3-2F0D-4B91-98F1-400300826A0A} = {B41BF331-FCCB-2ADF-CDB6-767964B34647} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {47442BB3-05F5-4AAF-A859-D456A81FC0A2} + EndGlobalSection +EndGlobal diff --git a/CSharp/AnP.csproj b/CSharp/AnP.csproj new file mode 100755 index 0000000..41964d3 --- /dev/null +++ b/CSharp/AnP.csproj @@ -0,0 +1,23 @@ + + + Exe + net10.0;net462 + enable + enable + latest + AnP + AnP + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + \ No newline at end of file diff --git a/CSharp/AnP.slnx b/CSharp/AnP.slnx new file mode 100755 index 0000000..939d01f --- /dev/null +++ b/CSharp/AnP.slnx @@ -0,0 +1,3 @@ + + + diff --git a/CSharp/Application/AnP.cs b/CSharp/Application/AnP.cs new file mode 100755 index 0000000..3ccf424 --- /dev/null +++ b/CSharp/Application/AnP.cs @@ -0,0 +1,9 @@ +using AnP.Interfaces.Application; + +namespace AnP.Application{ + class AnP:AnPInterface{ + + public AnP(object? inputs = null){} + + } +} \ No newline at end of file diff --git a/CSharp/Interfaces/Application/AnPInterface.cs b/CSharp/Interfaces/Application/AnPInterface.cs new file mode 100644 index 0000000..962bd67 --- /dev/null +++ b/CSharp/Interfaces/Application/AnPInterface.cs @@ -0,0 +1,5 @@ +namespace AnP.Interfaces.Application{ + public interface AnPInterface{ + + } +} \ No newline at end of file diff --git a/CSharp/Interfaces/Managers/I18NManagerInterface.cs b/CSharp/Interfaces/Managers/I18NManagerInterface.cs new file mode 100644 index 0000000..49193f4 --- /dev/null +++ b/CSharp/Interfaces/Managers/I18NManagerInterface.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Linq; + +namespace AnP.Interface.Managers{ + + public interface I18NManagerInterface{ + + public string get(List strings, object? inputs = null, List? languages = null, int custom_options = 0); + + public void add(object? items, int custom_options = 0); + + } + +} \ No newline at end of file diff --git a/CSharp/Managers/I18NManager.cs b/CSharp/Managers/I18NManager.cs new file mode 100644 index 0000000..0da2fc9 --- /dev/null +++ b/CSharp/Managers/I18NManager.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using AnP.Types; +using AnP.Utils; +using AnP.Interfaces.Application; + +namespace AnP.Managers{ + public class I18NManager{ + + private static readonly Dictionary> DEFAULT_SENTENCES = new Dictionary>{ + {"english", new Dictionary{ + {"greeting", new I18NSentenceType.Text("Hello, World!")} + }} + }; + + private static readonly Options GET_OPTIONS = new Options(); + + public AnPInterface anp; + private Dictionary> sentences; + private string language; + private string default_language; + + public I18NManager(AnPInterface anp){ + this.anp = anp; + sentences = new Dictionary>(DEFAULT_SENTENCES); + language = default_language = "english"; + } + + public string get(object strings, object? inputs = null, object? languages = null, int custom_options = 0){ + + List keys = Common.get_keys(strings); + Options options = new Options(custom_options, GET_OPTIONS); + + if(keys.Count != 0){ + + List languages_used = new List(); + + foreach(string language in Common.get_keys(languages).Concat( + new List{this.language, default_language} + ).Concat(sentences.Keys)) + if(!languages_used.Contains(language)){ + languages_used.Add(language); + if(sentences.ContainsKey(language)) + foreach(string key in keys) + if(sentences[language].ContainsKey(key)) + return sentences[language][key] switch{ + I18NSentenceType.Text text => text.value, + I18NSentenceType.List list => string.Join("", list.value), + _ => "" + }; + } + }; + return Common.get_strings(strings).FirstOrDefault() ?? ""; + } + + public void add(object? items, int custom_options = 0){} + + } +} \ No newline at end of file diff --git a/CSharp/Managers/SettingsManager.cs b/CSharp/Managers/SettingsManager.cs new file mode 100644 index 0000000..db628c2 --- /dev/null +++ b/CSharp/Managers/SettingsManager.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using AnP.Utils; +using AnP.Interfaces.Application; + +namespace AnP.Managers{ + public class SettingsManager{ + + public static readonly Dictionary DEFAULT_SETTINGS = new Dictionary{}; + public static readonly Options GET_OPTIONS = new Options(Options.ALLOW_NULLS); + public static readonly Options ADD_OPTIONS = new Options(Options.NO_OVERWRITE); + + public AnPInterface anp; + public Dictionary settings = new Dictionary(){}; + public Dictionary secrets = new Dictionary(){}; + public Dictionary inputs; + + public SettingsManager(AnPInterface anp, object? inputs = null){ + this.anp = anp; + this.inputs = Common.get_dictionary(inputs); + } + + public T? get(object keys, object? inputs, T? _default = default(T?), int custom_options = 0){ + return Common.get(keys, new object?[]{ + inputs, this.inputs, secrets, settings, DEFAULT_SETTINGS + }, _default, new Options(custom_options, GET_OPTIONS).get()); + } + + public void add(object? items, int custom_options = 0){} + + } +} \ No newline at end of file diff --git a/CSharp/Program.cs b/CSharp/Program.cs new file mode 100755 index 0000000..4db7df1 --- /dev/null +++ b/CSharp/Program.cs @@ -0,0 +1,9 @@ +using System; + +namespace AnP{ + class Program{ + static void Main(string[] args){ + Console.WriteLine("Hello World!"); + } + } +} \ No newline at end of file diff --git a/CSharp/Types/ColorType.cs b/CSharp/Types/ColorType.cs new file mode 100644 index 0000000..92b3d43 --- /dev/null +++ b/CSharp/Types/ColorType.cs @@ -0,0 +1,14 @@ +using AnP.Utils; + +namespace AnP.Types{ + public abstract record ColorType{ + + private ColorType(){} + + public sealed record String(string value):ColorType; + public sealed record List(IEnumerable value):ColorType; + public sealed record Integer(int value):ColorType; + public sealed record Instance(Color value):ColorType; + + } +} \ No newline at end of file diff --git a/CSharp/Types/I18NSentenceType.cs b/CSharp/Types/I18NSentenceType.cs new file mode 100644 index 0000000..c4c5fc7 --- /dev/null +++ b/CSharp/Types/I18NSentenceType.cs @@ -0,0 +1,10 @@ +namespace AnP.Types{ + public abstract record I18NSentenceType{ + + private I18NSentenceType(){} + + public sealed record Text(string value):I18NSentenceType; + public sealed record List(IEnumerable value):I18NSentenceType; + + } +} \ No newline at end of file diff --git a/CSharp/Utils/Check.cs b/CSharp/Utils/Check.cs new file mode 100755 index 0000000..6ddb603 --- /dev/null +++ b/CSharp/Utils/Check.cs @@ -0,0 +1,41 @@ +using System.Collections; +using System.Collections.Generic; + +namespace AnP.Utils{ + public class Check{ + + public static bool is_string(object? item){ + return item is string; + } + + + public static bool is_key(object? item){ + return item is string && RE.KEY.IsMatch((string)item); + } + + public static bool is_dictionary(object? item){ + return item is IDictionary; + } + + public static bool is_dictionary(object? item){ + return item is IDictionary; + } + + public static bool is_array(object? item){ + return item is IEnumerable; + } + + public static bool is_array(object? item){ + return item is IEnumerable; + } + + public static bool is_integer(object? item){ + return item is int; + } + + public static bool is_float(object? item){ + return item is float || item is double || item is decimal; + } + + } +} \ No newline at end of file diff --git a/CSharp/Utils/Color.cs b/CSharp/Utils/Color.cs new file mode 100755 index 0000000..f778896 --- /dev/null +++ b/CSharp/Utils/Color.cs @@ -0,0 +1,140 @@ +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; + +namespace AnP.Utils{ + public class Color{ + + public int red = 0x00; + public int green = 0x00; + public int blue = 0x00; + public int alpha = 0xFF; + + public Color(string code){ + + if(code.StartsWith("#")){ + + code = code.Substring(1); + + if(code.Length == 3 || code.Length == 4) + code = string.Concat(code.Select(c => $"{c}{c}")); + code = code.PadLeft(8, 'F'); + + alpha = Convert.ToInt32(code.Substring(0, 2), 16); + red = Convert.ToInt32(code.Substring(2, 2), 16); + green = Convert.ToInt32(code.Substring(4, 2), 16); + blue = Convert.ToInt32(code.Substring(6, 2), 16); + + }else if(code.StartsWith("rgb")){ + + string[] items = code.Substring(code.IndexOf("(") + 1, code.LastIndexOf(")") - code.IndexOf("(") - 1).Split(","); + + red = items.Length > 0 ? Convert.ToInt32(items[0]) : 0; + green = items.Length > 1 ? Convert.ToInt32(items[1]) : 0; + blue = items.Length > 2 ? Convert.ToInt32(items[2]) : 0; + alpha = items.Length > 3 ? ( + items[3].Contains(".") ? (int)(Convert.ToSingle(items[3]) * 0xFF) : + Convert.ToInt32(items[3])) : 0xFF; + + } + + } + + public Color(int color){ + alpha = 0xFF - ((color >> 24) & 0xFF); + red = (color >> 16) & 0xFF; + green = (color >> 8) & 0xFF; + blue = color & 0xFF; + } + + public Color(IEnumerable inputs, string type = "rgba"){ + + object[] array = ((IEnumerable)inputs).Cast().ToArray(); + + if(type == "rgb" || type == "rgba" || true){ + red = array.Length > 0 ? (int)array[0] : 0; + green = array.Length > 1 ? (int)array[1] : 0; + blue = array.Length > 2 ? (int)array[2] : 0; + alpha = array.Length > 3 ? ( + Check.is_integer(array[3]) ? (int)array[3] : + Check.is_float(array[3]) ? (int)((float)array[3] * 0xFF) : + 0xFF) : 0xFF; + } + + } + + public Color(Color color){ + red = color.red; + green = color.green; + blue = color.blue; + alpha = color.alpha; + } + + public string to_rgba(){ + return $"rgba({red}, {green}, {blue}, {(float)alpha / 0xFF:0.##})"; + } + + public string to_rgb(){ + return $"rgb({red}, {green}, {blue})"; + } + + public string to_hex(bool with_alpha = false){ + return $"#{(with_alpha ? $"{alpha:X2}" : "")}{red:X2}{green:X2}{blue:X2}"; + } + + public static Color mix(params object[] colors){ + if(colors.Length == 0) + return new Color(0); + + Color _base; + int l = colors.Length; + + if(Check.is_string(colors[0])) + _base = new Color((string)colors[0]); + else if(Check.is_integer(colors[0])) + _base = new Color((int)colors[0]); + else if(Check.is_array(colors[0])) + _base = new Color((IEnumerable)colors[0]); + else if(colors[0] is Color) + _base = new Color((Color)colors[0]); + else + return new Color(0); + + if(l != 1){ + + for(int i = 1; i < l; i ++){ + + Color subcolor; + + if(Check.is_string(colors[i])) + subcolor = new Color((string)colors[i]); + else if(Check.is_integer(colors[i])) + subcolor = new Color((int)colors[i]); + else if(Check.is_array(colors[i])) + subcolor = new Color((IEnumerable)colors[i]); + else if(colors[i] is Color) + subcolor = new Color((Color)colors[i]); + else + continue; + + _base.red += subcolor.red; + _base.green += subcolor.green; + _base.blue += subcolor.blue; + _base.alpha += subcolor.alpha; + + } + + _base.red /= l; + _base.green /= l; + _base.blue /= l; + _base.alpha /= l; + + }; + + return _base; + + } + + } +} \ No newline at end of file diff --git a/CSharp/Utils/Common.cs b/CSharp/Utils/Common.cs new file mode 100755 index 0000000..327913e --- /dev/null +++ b/CSharp/Utils/Common.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; + +namespace AnP.Utils{ + public class Common{ + + public static readonly Options GET_DICTIONARY_OPTIONS = new Options(Options.NO_OVERWRITE); + public static readonly Options GET_OPTIONS = new Options(Options.ALLOW_NULLS); + + public static List get_strings(object? items){ + + List strings = new List(); + + if(items != null){ + if(Check.is_string(items)) + strings.Add((string)items); + else if(Check.is_array(items)) + foreach(object item in (IEnumerable)items) + if(Check.is_string(item)) + strings.Add((string)item); + } + + return strings; + } + + public static List get_keys(object? items){ + + List keys = new List(); + + if(items != null){ + if(Check.is_key(items)) + keys.Add((string)items); + else if(Check.is_array(items)) + foreach(object item in (IEnumerable)items) + if(Check.is_key(item)){ + + string key = (string)item; + + if(!keys.Contains(key)) + keys.Add(key); + + }; + } + + return keys; + } + + public static List> get_dictionaries(object? items){ + + List> dictionaries = new List>(); + + if(items != null){ + if(Check.is_dictionary(items)) + dictionaries.Add((Dictionary)items); + else if(Check.is_array(items)) + foreach(object item in (IEnumerable)items) + if(Check.is_dictionary(item)) + dictionaries.Add((IDictionary)item); + } + + return dictionaries; + } + + public static Dictionary get_dictionary(object? items, int custom_options = 0){ + + Dictionary dictionary = new Dictionary(); + Options options = new Options(custom_options, GET_DICTIONARY_OPTIONS); + + if(items != null){ + if(Check.is_dictionary(items)) + dictionary = (Dictionary)items; + else if(Check.is_array(items)) + foreach(object item in (IEnumerable)items) + if(Check.is_dictionary(item)) + foreach(KeyValuePair pair in (IDictionary)item) + if(options.overwrite == true || !dictionary.ContainsKey(pair.Key)) + dictionary[pair.Key] = pair.Value; + } + + return dictionary; + } + + public static T? get(object keys, object inputs, T? _default = default(T?), int custom_options = 0){ + + Options options = new Options(custom_options, GET_OPTIONS); + List keys_list = get_keys(keys); + + if(keys_list.Count != 0) + foreach(IDictionary subinputs in get_dictionaries(inputs)) + foreach(string key in keys_list) + if(subinputs.ContainsKey(key) && ( + options.allow_null == true || + subinputs[key] != null + )) + return subinputs[key]; + return _default; + } + + } +} \ No newline at end of file diff --git a/CSharp/Utils/Options.cs b/CSharp/Utils/Options.cs new file mode 100755 index 0000000..580add8 --- /dev/null +++ b/CSharp/Utils/Options.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; + +namespace AnP.Utils{ + public class Options{ + + public const int OVERWRITING = 0; + public static readonly int OVERWRITE = 1 * (int)Math.Pow(3, OVERWRITING); + public static readonly int NO_OVERWRITE = 2 * (int)Math.Pow(3, OVERWRITING); + + public const int NULLISH = 1; + public static readonly int ALLOW_NULLS = 1 * (int)Math.Pow(3, NULLISH); + public static readonly int NO_NULLS = 2 * (int)Math.Pow(3, NULLISH); + + public static readonly Options DEFAULT = new Options(NO_OVERWRITE + ALLOW_NULLS); + + public static bool? get(int value, int option){ + + int ternary = value / (int)Math.Pow(3, option) % 3; + + return ( + ternary == 1 ? true : + ternary == 2 ? false : + null); + } + + public bool? overwrite = null; + public bool? allow_null = null; + + public Options(int value = 0, Options? _default = null){ + + if(_default == null) + _default = DEFAULT; + + overwrite = get(value, OVERWRITING) ?? _default?.overwrite; + allow_null = get(value, NULLISH) ?? _default?.allow_null; + + } + + public int get(){ + + int code = 0; + + foreach(KeyValuePair pair in new Dictionary{ + {OVERWRITING, overwrite}, + {NULLISH, allow_null} + }) + code += ( + pair.Value == true ? 1 : + pair.Value == false ? 2 : + 0) * (int)Math.Pow(3, pair.Key); + + return code; + } + + } +} \ No newline at end of file diff --git a/CSharp/Utils/Patterns.cs b/CSharp/Utils/Patterns.cs new file mode 100755 index 0000000..e7b1576 --- /dev/null +++ b/CSharp/Utils/Patterns.cs @@ -0,0 +1,7 @@ +using System.Text.RegularExpressions; + +namespace AnP.Utils{ + public class RE{ + public static readonly Regex KEY = new Regex(@"^[a-z_][a-z0-9_]*$", RegexOptions.IgnoreCase); + } +} \ No newline at end of file diff --git a/DotNET/Dockerfile b/DotNET/Dockerfile new file mode 100644 index 0000000..69e1fda --- /dev/null +++ b/DotNET/Dockerfile @@ -0,0 +1,5 @@ +from mcr.microsoft.com/dotnet/sdk:10.0 +env dotnet_cli_telemetry_output=1 +run apt update && apt install -y procps build-essential cmake g++ gdb git +run rm -rf /var/lib/apt/lists/* +workdir /workspace \ No newline at end of file diff --git a/DotNET/docker.rebuild.sh b/DotNET/docker.rebuild.sh new file mode 100755 index 0000000..31bf04a --- /dev/null +++ b/DotNET/docker.rebuild.sh @@ -0,0 +1,4 @@ +#!/bin/bash +directory=`dirname $(readlink -f "$0")` +[ "$(docker images -q anp:dotnet 2>/dev/null)" ] && docker image remove anp:dotnet --force +docker build -f $directory/Dockerfile -t anp:dotnet $directory --no-cache \ No newline at end of file diff --git a/DotNET/scripts/entrypoint.sh b/DotNET/scripts/entrypoint.sh new file mode 100755 index 0000000..18df309 --- /dev/null +++ b/DotNET/scripts/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +trap "exit 0" INT TERM + +while true; do + sleep 1 & wait $! +done \ No newline at end of file diff --git a/JSON/AnP.settings.json b/JSON/AnP.settings.json new file mode 100644 index 0000000..0837ee4 --- /dev/null +++ b/JSON/AnP.settings.json @@ -0,0 +1,69 @@ +{ + + "anp_start" : null, + "anp_show_building_message" : true, + "anp_show_built_message" : true, + "anp_show_starting_message" : true, + "anp_show_already_started_message" : true, + "anp_show_started_message" : true, + "anp_show_updating_message" : true, + "anp_show_updated_message" : true, + "anp_show_closing_message" : true, + "anp_show_already_closed_message" : true, + "anp_show_closed_message" : true, + "print_format" : "[{type}] {yyyy}{mm}{dd} {hh}:{ii}:{ss} [{line}]{file_relative}({method}): {message}", + "exception_format" : " '{file}({method})[{line}]'{lines}\n\n{message}", + "trace_line_format" : " [{i}] {trace_line}", + "autostart" : true, + "anp_end" : null, + + "anp_print_types_manager_start" : null, + "default_print_types" : [ + ["#888", ["unkn", "unknown"]], + ["#00F", ["info", "information", "default"]], + ["#FF0", ["warn", "warning", "caution"]], + ["#F00", ["erro", "err", "no", "x", "n", "error", "panic", "danger", "critical"]], + ["#0F0", [" ok ", "ok", "yes", "y", "success", "done", "complete"]], + ["#888", ["test", "debug", "trace", "debugging"]], + ["#F00", ["exce", "except", "exception"]] + ], + "anp_print_types_manager_show_building_message" : true, + "anp_print_types_manager_show_built_message" : true, + "anp_print_types_manager_show_starting_message" : true, + "anp_print_types_manager_show_already_started_message" : true, + "anp_print_types_manager_show_started_message" : true, + "anp_print_types_manager_show_updating_message" : true, + "anp_print_types_manager_show_updated_message" : true, + "anp_print_types_manager_show_closing_message" : true, + "anp_print_types_manager_show_already_closed_message" : true, + "anp_print_types_manager_show_closed_message" : true, + "anp_print_types_manager_end" : null, + + "anp_settings_manager_start" : null, + "default_settings_files" : "JSON/AnP.settings.json", + "default_secrets_files" : "JSON/AnP.secrets.json", + "default_value" : null, + "allow_nulls" : true, + "default_text" : "", + "anp_settings_manager_show_building_message" : true, + "anp_settings_manager_show_built_message" : true, + "anp_settings_manager_show_starting_message" : true, + "anp_settings_manager_show_already_started_message" : true, + "anp_settings_manager_show_started_message" : true, + "anp_settings_manager_show_updating_message" : true, + "anp_settings_manager_show_updated_message" : true, + "anp_settings_manager_show_closing_message" : true, + "anp_settings_manager_show_already_closed_message" : true, + "anp_settings_manager_show_closed_message" : true, + "anp_settings_manager_add_show_ok" : false, + "anp_settings_manager_add_show_errors" : true, + "anp_settings_manager_add_check_errors" : true, + "anp_settings_manager_add_secrets_show_ok" : false, + "anp_settings_manager_add_secrets_show_errors" : true, + "anp_settings_manager_add_secrets_check_errors" : true, + "anp_settings_manager_get_show_ok" : false, + "anp_settings_manager_get_show_errors" : true, + "anp_settings_manager_get_check_errors" : true, + "anp_settings_manager_end" : null + +} \ No newline at end of file diff --git a/JSON/I18N/AnP.i18n.espanol.json b/JSON/I18N/AnP.i18n.espanol.json new file mode 100644 index 0000000..04fb2fe --- /dev/null +++ b/JSON/I18N/AnP.i18n.espanol.json @@ -0,0 +1,44 @@ +{ + "espanol" : { + + "anp_start" : null, + "anp_building" : "La aplicación AnP se está construyendo...", + "anp_built" : "La aplicación AnP se ha construido.", + "anp_starting" : "La aplicación AnP se está iniciando...", + "anp_already_started" : "La aplicación AnP ya se ha iniciado.", + "anp_started" : "La aplicación AnP se ha iniciado.", + "anp_updating" : "La aplicación AnP se está actualizando...", + "anp_updated" : "La aplicación AnP se ha actualizado.", + "anp_closing" : "La aplicación AnP se está cerrando...", + "anp_already_closed" : "La aplicación AnP ya se ha cerrado.", + "anp_closed" : "La aplicación AnP se ha cerrado.", + "anp_end" : null, + + "anp_print_types_manager_start" : null, + "anp_print_types_manager_building" : "El Administrador de Tipos de Impresión se está construyendo...", + "anp_print_types_manager_built" : "El Administrador de Tipos de Impresión se ha construido.", + "anp_print_types_manager_starting" : "El Administrador de Tipos de Impresión se está iniciando...", + "anp_print_types_manager_already_started" : "El Administrador de Tipos de Impresión ya se ha iniciado.", + "anp_print_types_manager_started" : "El Administrador de Tipos de Impresión se ha iniciado.", + "anp_print_types_manager_updating" : "El Administrador de Tipos de Impresión se está actualizando...", + "anp_print_types_manager_updated" : "El Administrador de Tipos de Impresión se ha actualizado.", + "anp_print_types_manager_closing" : "El Administrador de Tipos de Impresión se está cerrando...", + "anp_print_types_manager_already_closed" : "El Administrador de Tipos de Impresión ya se ha cerrado.", + "anp_print_types_manager_closed" : "El Administrador de Tipos de Impresión se ha cerrado.", + "anp_print_types_manager_end" : null, + + "anp_settings_manager_start" : null, + "anp_settings_manager_building" : "El Administrador de Configuraciones se está construyendo...", + "anp_settings_manager_built" : "El Administrador de Configuraciones se ha construido.", + "anp_settings_manager_starting" : "El Administrador de Configuraciones se está iniciando...", + "anp_settings_manager_already_started" : "El Administrador de Configuraciones ya se ha iniciado.", + "anp_settings_manager_started" : "El Administrador de Configuraciones se ha iniciado.", + "anp_settings_manager_updating" : "El Administrador de Configuraciones se está actualizando...", + "anp_settings_manager_updated" : "El Administrador de Configuraciones se ha actualizado.", + "anp_settings_manager_closing" : "El Administrador de Configuraciones se está cerrando...", + "anp_settings_manager_already_closed" : "El Administrador de Configuraciones ya se ha cerrado.", + "anp_settings_manager_closed" : "El Administrador de Configuraciones se ha cerrado.", + "anp_settings_manager_end" : null + + } +} \ No newline at end of file diff --git a/Public/ecma/Abstracts/BaseAbstract.ecma.js b/Public/ecma/Abstracts/BaseAbstract.ecma.js new file mode 100644 index 0000000..ded4c74 --- /dev/null +++ b/Public/ecma/Abstracts/BaseAbstract.ecma.js @@ -0,0 +1,190 @@ +"use strict"; + +/** + * @typedef {import("../Application/AnP.ecma.js").AnP} AnP + */ + +import {Utils} from "../Utils/Utils.ecma"; +import {Check} from "../Utils/Check.ecma"; + +/** + * @abstract + * @class BaseAbstract + * @constructor + * @param {!any} base + * @param {!AnP} anp + * @param {!string} key + * @param {?(Object.|Array.)} [inputs = null] + * @param {!Object.} [print_options = {}] + * @returns {void} + * @access public + * @static + */ +export const BaseAbstract = (function(){ + + /** + * @callable base_abstract_callback_end + * @param {!boolean} ok + * @returns {boolean} + */ + + /** + * @callable base_abstract_get_value + * @param {!(string|Array.)} keys + * @param {?(Object.|Array.)} [inputs = null] + * @param {?any} [default_value = null] + * @returns {any|null} + */ + + /** + * @constructs BaseAbstract + * @param {!any} base + * @param {!AnP} anp + * @param {?(Object.|Array.)} [inputs = null] + * @param {!string} key + * @param {!Object.} [print_options = {}] + * @returns {void} + * @access private + * @static + */ + const BaseAbstract = function(base, anp, key, inputs = null, print_options = {}){ + + /** @type {BaseAbstract} */ + const self = this; + + /** + * @returns {boolean} + * @access public + */ + this._build = () => true; + + /** + * @param {!string} type + * @param {!(string|Array.)} message + * @param {?(Object.|Array.)} [inputs = null] + * @param {!number} [i = 0] + * @returns {void} + * @access public + */ + this.print = (type, message, inputs = null, i = 0) => { + self.allow_print.message === false || + anp.print(type, key + "_" + message, [print_options, inputs], i + 1); + }; + + /** + * @returns {void} + * @private + */ + const constructor = () => { + + /** @type {base_abstract_get_value} */ + const get_value = anp.settings ? anp.settings.get : Utils.get_value; + + /** @type {Object.} */ + this.allow_print = [ + "building", "built", + "starting", "started", "already_started", + "closing", "closed", "already_closed" + ].reduce((item, action) => { + + item[key] = get_value(key + "_print_" + action + "_message", inputs, true); + + return item; + }, {}); + + self.print("info", "building"); + + this.key = key; + /** @type {boolean} */ + this.started = false; + /** @type {boolean} */ + this.closed = true; + + self.extends(base); + + self._build(); + + self.print("ok", "built"); + + }; + + /** + * @param {!any} item + * @returns {void} + * @access public + */ + this.extends = item => { + Utils.extends(self, item); + }; + + /** + * @param {?base_abstract_callback_end} [callback = null] + * @returns {boolean} + * @access public + */ + this._start = (callback = null) => Check.is_function(callback) ? callback(true) : true; + + /** + * @param {?base_abstract_callback_end} [callback = null] + * @returns {boolean} + * @access public + */ + this.start = (callback = null) => { + + /** @type {base_abstract_callback_end} */ + const end = ok => Check.is_function(callback) ? callback(ok) : ok; + + self.print("info", "starting"); + + if(self.started){ + self.print("warn", "already_started"); + return end(false); + }; + self.started = true; + + return self._start(ok => { + self.closed = false; + self.print("ok", "started"); + return end(ok); + }); + }; + + /** + * @param {?base_abstract_callback_end} [callback = null] + * @returns {boolean} + * @access public + */ + this._close = (callback = null) => Check.is_function(callback) ? callback(true) : true; + + /** + * @param {?base_abstract_callback_end} [callback = null] + * @returns {boolean} + * @access public + */ + this.close = (callback = null) => { + + /** @type {base_abstract_callback_end} */ + const end = ok => Check.is_function(callback) ? callback(ok) : ok; + + self.print("info", "closing"); + + if(self.closed){ + self.print("warn", "already_closed"); + return end(false); + }; + self.closed = true; + + return self._close(ok => { + self.started = false; + self.print("ok", "closed"); + return end(ok); + }); + + }; + + constructor(); + + }; + + return BaseAbstract; +})(); \ No newline at end of file diff --git a/Public/ecma/Application/AnP.ecma.js b/Public/ecma/Application/AnP.ecma.js new file mode 100644 index 0000000..5ba7d85 --- /dev/null +++ b/Public/ecma/Application/AnP.ecma.js @@ -0,0 +1,67 @@ +"use strict"; + +import {I18NManager} from "../Managers/I18NManager.ecma.js"; +import {SettingsManager} from "../Managers/SettingsManager.ecma.js"; +import {Components} from "./Components.ecma.js"; +import {FilesDriver} from "../Drivers/FilesDriver.ecma.js"; + +/** + * @class AnP + * @constructor + * @param {?(Object.|Array.)} [inputs = null] + * @returns {void} + * @access public + * @static + */ +export const AnP = (function(){ + + /** + * @constructs AnP + * @param {?(Object.|Array.)} [inputs = null] + * @returns {void} + * @access private + * @static + */ + const AnP = function(inputs = null){ + + /** @type {AnP} */ + const self = this; + /** @type {boolean} */ + let started = false, + /** @type {boolean} */ + closed = false; + + /** @type {SettingsManager}*/ + this.settings = new SettingsManager(self, inputs); + /** @type {I18NManager}*/ + this.i18n = new I18NManager(self); + /** @type {Components} */ + this.comp = this.components = new Components(self); + /** @type {FilesDriver} */ + this.files = new FilesDriver(self); + + /** + * @returns {void} + * @access private + */ + const constructor = () => {}; + + this.start = (callback = null) => {}; + + /** + * @param {!any} module + * @returns {void} + * @access public + */ + this.extends = module => { + for(const key in self) + module[key] === undefined && + (module[key] = self[key]); + }; + + constructor(); + + }; + + return AnP; +})(); \ No newline at end of file diff --git a/Public/ecma/Application/Components.ecma.js b/Public/ecma/Application/Components.ecma.js new file mode 100644 index 0000000..014cb8a --- /dev/null +++ b/Public/ecma/Application/Components.ecma.js @@ -0,0 +1,412 @@ +"use strict"; + +import {Check} from "../Utils/Check.ecma.js"; + +/** + * @typedef {import("../Application/AnP.ecma.js").AnP} AnP + */ + +/** + * @class Components + * @constructor + * @param {!AnP} anp + * @returns {void} + * @access public + * @static + */ +export const Components = (function(){ + + /** + * @callback components_event_callback + * @param {!HTMLElement} item + * @param {!Event} event + * @returns {boolean|void} + */ + + /** + * @constructs Components + * @param {!AnP} anp + * @returns {void} + * @access private + * @static + */ + const Components = function(anp){ + + /** @type {Components} */ + const self = this; + + /** + * @returns {void} + * @access private + */ + const constructor = () => {}; + + /** + * @param {!(string|Array.)} i18n + * @param {!string} [tag = "span"] + * @param {?string} [_default = null] + * @returns {[string, Object., string]} + * @access public + */ + this.i18n = (i18n, tag = "span", _default = null) => [tag, {data_i18n : i18n}, anp.i18n.get(i18n, null, _default)]; + + /** + * @param {!string} name + * @param {!string} [tag = "span"] + * @returns {[string, Object.]} + * @access public + */ + this.icon = (name, tag = "span") => [tag, {data_icon : name}]; + + /** + * @param {!string} name + * @param {?components_event_callback} [on_click = null] + * @param {!string} [type = "button"] + * @param {?string} [default_text = null] + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + * @access public + */ + this.button = (name, on_click = null, type = "button", default_text) => ["button", { + type : type, + data_i18n : name, + data_i18n_without : true, + title : anp.i18n.get(name, null, default_text), + ...(on_click ? {on_click : on_click} : {}) + }, [ + self.icon(name), + self.i18n(name, "span", default_text) + ]]; + + /** + * @param {!string} type + * @param {!string} name + * @param {!boolean} checked + * @param {?components_event_callback} on_change + * @param {?string} default_text + * @param {!boolean} [role_button = false] + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + * @access private + */ + const select_item = (type, name, checked, on_change, default_text, role_button = false) => { + + /** @type {string} */ + const id = name.slice(-2) == "[]" ? anp.identifiers.get() : name; + + return ["label", { + for : id, + class : type + "-input", + data_i18n : name, + data_i18n_without : true, + title : anp.i18n.get(name, null, default_text), + ...(role_button ? {role : "button"} : {}) + }, [ + ["input", { + type : type, + id : id, + name : name, + ...(on_change ? {on_change : on_change} : {}), + ...(checked ? {checked : "checked"} : {}) + }], + self.icon(type, "span"), + self.i18n(name, "span", default_text) + ]]; + }; + + /** + * @param {!string} name + * @param {!boolean} [checked = false] + * @param {?components_event_callback} [on_change = null] + * @param {?string} [default_text = null] + * @param {!boolean} [role_button = false] + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + */ + this.checkbox = (name, checked = false, on_change = null, default_text, role_button = false) => ( + select_item("checkbox", name, checked, on_change, default_text, role_button) + ); + + /** + * @param {!string} name + * @param {!boolean} [checked = false] + * @param {?components_event_callback} [on_change = null] + * @param {?string} [default_text = null] + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + */ + this.radio = (name, checked = false, on_change = null, default_text) => ( + select_item("radio", name, checked, on_change, default_text) + ); + + /** + * @param {!string} name + * @param {?number} [value = null] + * @param {?number} [minimum = null] + * @param {?number} [maximum = null] + * @param {?number} [step = null] + * @param {?components_event_callback} [on_change = null] + * @param {?string} [default_text = null] + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + * @access public + */ + this.number = (name, value = null, minimum = null, maximum = null, step = null, on_change = null, default_text) => { + + /** @type {string} */ + const text = anp.i18n.get(name, null, default_text); + + return ["label", { + for : name, + class : "number-input", + data_i18n : name, + data_i18n_without : true, + title : text, + }, [ + ["input", { + type : "number", + id : name, + name : name, + ...(value !== null ? {value : value} : {}), + ...(minimum !== null ? {min : minimum} : {}), + ...(maximum !== null ? {max : maximum} : {}), + ...(step !== null ? {step : step} : {}), + ...(on_change ? {on_change : on_change} : {}), + data_i18n : name, + data_i18n_without : true, + placeholder : text + "..." + }], + ["span", {class : "minimum"}, minimum || "-∞"], + ["span", {class : "maximum"}, maximum || "∞"] + ]]; + }; + + /** + * @param {!string} type + * @param {!string} name + * @param {?string} [value = null] + * @param {?number} [maximum_length = null] + * @param {?string} [pattern = null] + * @param {?components_event_callback} [on_change = null] + * @param {?string} [default_text = null] + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + * @access private + */ + const text = (type, name, value = null, maximum_length = null, pattern = null, on_change = null, default_text = null) => { + + /** @type {string} */ + const text = anp.i18n.get(name, null, default_text); + + return ["label", { + for : name, + class : "input-" + type + }, [ + ["input", { + type : type, + id : name, + name : name, + ...(value !== null ? {value : value} : {}), + ...(pattern !== null ? {pattern : pattern} : {}), + ...(on_change ? {on_input : on_change} : {}), + data_i18n : name, + data_i18n_without : true, + placeholder : text + "..." + }], + ["span", {class : "length"}, 0], + ...(maximum_length ? [["span", {class : "maximum-length"}, maximum_length || "∞"]] : []) + ]]; + }; + + /** + * @param {!string} name + * @param {?string} [value = null] + * @param {?number} [maximum_length = null] + * @param {?string} [pattern = null] + * @param {?components_event_callback} [on_change = null] + * @param {?string} [default_text = null] + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + * @access public + */ + this.text = (name, value = null, maximum_length = null, pattern = null, on_change = null, default_text = null) => ( + text("text", name, value, maximum_length, pattern, on_change, default_text) + ); + + /** + * @param {!string} name + * @param {?string} [value = null] + * @param {?number} [maximum_length = null] + * @param {?string} [pattern = null] + * @param {?components_event_callback} [on_change = null] + * @param {?string} [default_text = null] + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + * @access public + */ + this.password = (name, value = null, maximum_length = null, pattern = null, on_change = null, default_text = null) => ( + text("password", name, value, maximum_length, pattern, on_change, default_text) + ); + + /** + * @param {...[string, components_event_callback|null, string, string|null]} buttons + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + * @access public + */ + this.buttons = (...buttons) => ["div", {class : "buttons"}, buttons.map(button => self.button(...button))]; + + /** + * @param {!string} name + * @param {...any} items + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + */ + this.group = (name, ...items) => ["div", { + class : "group", + data_i18n : name, + data_i18n_without : true, + title : anp.i18n.get(name) + }, items]; + + /** + * @param {!string} name + * @param {!Array.} structure + * @param {?components_event_callback} on_submit + * @param {...(components_event_callback|null)} [extra_actions] + * @returns {[string, Object., Array.<(string|[string, Object., string])>]} + * @access public + */ + this.form = (name, structure, on_submit, ...extra_actions) => ["form", { + class : "form", + data_name : name, + method : "get", + action : "#", + ...(on_submit ? {on_submit : on_submit} : {}) + }, [ + ["fieldset", {}, [ + self.i18n(name, "legend"), + self.i18n(name + "_text", "p"), + ["div", {class : "structure"}, structure.map(([type, name, ...item], i) => { + return ["div", { + data_i : i, + data_type : type, + data_i18n : name, + data_i18n_without : true, + title : anp.i18n.get(name, null, item[1] || name) + }, [ + ["label", {for : name}, [ + self.i18n(name), + self.i18n(name + "_description") + ]], + ["span", {class : "input"}, self[type] ? [self[type](name, ...item)] : item], + ["ul", {class : "errors"}] + ]]; + })], + ["ul", {class : "form-errors"}], + self.buttons( + ["clean", null, "clean"], + ...extra_actions, + on_submit ? ["submit", null, "submit"] : null + ) + ]] + ]]; + + /** + * @param {!HTMLElement} form + * @returns {HTMLFormElement|null} + * @access public + */ + this.get_form = form => { + + if(form) + while(form.tagName.toLowerCase() != "form" && (form = form.parentNode)); + + return form; + }; + + /** + * @param {!HTMLElement} form + * @returns {Object.)>} + * @access public + */ + this.get_form_data = form => ( + [...self.get_form(form).querySelectorAll("[name]")].reduce((data, item) => { + + /** @type {string} */ + let name_value = item.getAttribute("name"); + /** @type {string} */ + const name = name_value.replace(/\[\]$/i, ""), + /** @type {boolean} */ + is_array = name_value != name; + + is_array && !(name in data) && (data[name] = []); + + switch(item.getAttribute("type")){ + case "radio": + data[name] == [] && (data[name] = false); + item.checked && (data[name] = Number(item.value)); + break; + case "checkbox": + if(is_array) + data[name].push(item.checked); + else + data[name] = item.checked; + break; + case "number": + if(is_array) + data[name].push(Number(item.value)); + else + data[name] = Number(item.value); + break; + default: + if(is_array) + data[name].push(item.value); + else + data[name] = item.value; + break; + }; + + return data; + }, {}) + ); + + /** + * @param {!(string|Array.)} sources + * @param {?(string|Array.)} [i18n = null] + * @param {?string} [default_text = null] + * @returns {[string, Object., (string|[string, Object., string])>]} + * @access public + */ + this.image = (sources, i18n = null, default_text = null) => { + + /** @type {number} */ + let i = 0; + + Check.is_array(sources) || (sources = [sources]); + + return ["span", { + class : "image", + data_status : "loading", + ...( + i18n ? {data_i18n : i18n, title : anp.i18n.get(i18n, null, default_text)} : + default_text ? {title : default_text} : + {}), + }, [ + ["img", { + src : sources[i], + ...( + i18n ? {data_i18n : i18n, alt : anp.i18n.get(i18n, null, default_text)} : + default_text ? {alt : default_text} : + {}), + on_error : (image, event) => { + if(i >= sources.length) + image.parentNode.setAttribute("data-status", "error"); + else + image.parentNode.setAttribute("src", sources[++ i]); + }, + on_load : (image, event) => { + image.parentNode.setAttribute("data-status", "loaded"); + image.parentNode.querySelector("span").style.backgroundImage = "url('" + sources[i] + "')"; + } + }], + ["span"] + ]]; + }; + + constructor(); + + }; + + return Components; +})(); \ No newline at end of file diff --git a/Public/ecma/Drivers/FilesDriver.ecma.js b/Public/ecma/Drivers/FilesDriver.ecma.js new file mode 100644 index 0000000..e177b0a --- /dev/null +++ b/Public/ecma/Drivers/FilesDriver.ecma.js @@ -0,0 +1,155 @@ +"use strict"; + +/** + * @typedef {import("../Application/AnP.ecma.js").AnP} AnP + */ + +/** + * @class FilesDriver + * @constructor + * @param {!AnP} anp + * @returns {void} + * @access public + * @static + */ +export const FilesDriver = (function(){ + + /** + * @callback files_driver_start_callback + * @param {!boolean} ok + * @returns {boolean} + */ + + /** + * @constructs FilesDriver + * @param {!AnP} anp + * @returns {void} + * @access private + * @static + */ + const FilesDriver = function(anp){ + + /** @type {FilesDriver} */ + const self = this; + /** @type {boolean} */ + let started = false, + /** @type {boolean} */ + closed = false, + /** @type {string} */ + default_http_method = "GET", + /** @type {boolean} */ + default_http_asynchronous = true, + /** @type {number} */ + default_timeout = 2000; + + /** + * @returns {void} + * @access private + */ + const constructor = () => { + + self.update(); + + }; + + /** + * @param {?files_driver_start_callback} [callback = null] + * @returns {boolean} + * @access public + */ + this.update = (callback = null) => { + + default_http_method = anp.settings.get([ + "http_method", "default_http_method" + ], null, default_http_method); + default_http_asynchronous = anp.settings.get([ + "http_asynchronous", "default_http_asynchronous" + ], null, default_http_asynchronous); + default_timeout = anp.settings.get([ + "timeout", "default_timeout" + ], null, default_timeout); + + Utils.execute(callback); + + return true; + }; + + /** + * @param {?files_driver_start_callback} [callback = null] + * @returns {boolean} + * @access public + */ + this.start = (callback = null) => { + + if(started){ + return false; + }; + started = true; + + self.update(callback); + + return true; + }; + + this.load = (path, callback = null, inputs = null) => { + + /** @type {boolean} */ + let ended = false, + /** @type {number} */ + error = 0; + /** @type {XMLHttpRequest} */ + const ajax = new XMLHttpRequest(), + /** @type {number} */ + timeout = Utils.get_value([ + "timeout", "ajax_timeout", "default_timeout" + ], inputs, default_timeout), + /** @type {number} */ + date = Date.now(), + end = code => { + code && (error |= 1 << code); + !ended && (ended = true) && + Utils.execute( + callback, + ajax.responseText, + ajax.status, + ajax.readyState, + error, + error === 0 + ); + }; + + ajax.open( + Utils.get_value([ + "method", "http_method", "default_http_method" + ], inputs, default_http_method), + path, + Utils.get_value([ + "asynchronous", "http_asynchronous", "default_http_asynchronous" + ], inputs, default_http_asynchronous) + ); + ajax.timeout = timeout; + ajax.onreadystatechange = function(){ + if(ended) + return; + if(ajax.readyState === 4) + end(( + ajax.status >= 200 && ajax.status < 300 + ) || [301, 302, 304].includes(ajax.status) ? 0 : 1); + else if(Date.now() - date >= timeout) + end(2); + }; + ajax.send(null); + + ajax.onerror = () => {end(3);} + ajax.onabort = () => {end(4);} + ajax.ontimeout = () => {end(5);} + + return ajax; + }; + + constructor(); + + }; + + return FilesDriver; +})(); \ No newline at end of file diff --git a/Public/ecma/Managers/I18NManager.ecma.js b/Public/ecma/Managers/I18NManager.ecma.js new file mode 100644 index 0000000..2fe360d --- /dev/null +++ b/Public/ecma/Managers/I18NManager.ecma.js @@ -0,0 +1,139 @@ +"use strict"; + +import {Check} from "../Utils/Check.ecma.js"; +import {Utils} from "../Utils/Utils.ecma.js"; + +/** + * @typedef {import("../Application/AnP.ecma.js").AnP} AnP + */ + +/** + * @class I18NManager + * @constructor + * @param {!AnP} anp + * @returns {void} + * @access public + * @static + */ +export const I18NManager = (function(){ + + /** + * @callback i18n_manager_default_callback + * @returns {void} + */ + + /** + * @constructs I18NManager + * @param {!AnP} anp + * @returns {void} + * @access private + * @static + */ + const I18NManager = function(anp){ + + /** @type {I18NManager} */ + const self = this, + /** @type {Object.>} */ + sentences = {}; + /** @type {string} */ + let language_selected = "espanol", + /** @type {string} */ + default_language = "espanol"; + + /** + * @returns {void} + * @access private + */ + const constructor = () => { + + language_selected = anp.settings.get(["language_selected", "default_language"], null, language_selected); + default_language = anp.settings.get(["default_language", "language_selected"], null, default_language); + + }; + + /** + * @param {?i18n_manager_default_callback} [callback = null] + * @returns {void} + * @access public + */ + this.start = (callback = null) => { + Utils.execute_array([ + "default_i18n_files", "i18n_files" + ], (key, next_callback) => { + self.add(anp.settings.get(key), next_callback, true); + }, callback); + }; + + /** + * @param {!(string|Array.)} texts + * @param {?(Object.|Array.)} [variables = null] + * @param {?any} [_default = null] + * @returns {string|null} + * @access public + */ + this.get = (texts, variables = null, _default = null) => { + + /** @type {string|Array.|null} */ + let text = null; + /** @type {Array.} */ + const used = [], + /** @type {Array.} */ + languages = [language_selected, default_language].concat(Object.keys(sentences)), + /** @type {Array.} */ + keys = Utils.get_keys(texts); + + if(keys.length) + for(const language of languages){ + if(!used.includes(language) && sentences[language]) + for(const key of keys){ + if(sentences[language][key] !== undefined){ + text = sentences[language][key]; + break; + }; + }; + if(text !== undefined) + break; + used.push(language); + }; + + text === null && (text = ( + _default !== null ? _default : + texts !== null ? ( + Check.is_string(texts) ? texts : + Check.is_array(texts) && texts.length ? texts[0] : + null) : + null)) + + return Utils.string_variables(( + Check.is_array(text) ? text.join("") : + text), variables, _default); + }; + + /** + * @param {!(Object.|null>>|Array.|string)} inputs + * @param {?i18n_manager_default_callback} [callback = null] + * @param {!boolean} [overwrite = false] + * @returns {void} + * @access public + */ + this.add = (inputs, callback = null, overwrite = false) => { + anp.load_json(inputs, (item, next_callback) => { + for(const language in item){ + sentences[language] || (sentences[language] = {}); + for(const key in item[language]) + (overwrite || Check.is_null_or_undefined(sentences[language][key])) && + (sentences[language][key] = item[language][key]); + }; + next_callback(); + }, callback, true); + }; + + constructor(); + + }; + + /** @type {Object.} */ + I18NManager.DEFAULT_SETTINGS = {}; + + return I18NManager; +})(); \ No newline at end of file diff --git a/Public/ecma/Managers/IdentifiersManager.ecma.js b/Public/ecma/Managers/IdentifiersManager.ecma.js new file mode 100644 index 0000000..21c7642 --- /dev/null +++ b/Public/ecma/Managers/IdentifiersManager.ecma.js @@ -0,0 +1,89 @@ +"use strict"; + +import {Utils} from "../Utils/Utils.ecma.js"; + +/** + * @typedef {import("../Application/AnP.ecma.js").AnP} AnP + */ + +/** + * @class IdentifiersManager + * @constructor + * @param {!AnP} anp + * @returns {void} + * @access public + * @static + */ +export const IdentifiersManager = (function(){ + + /** + * @constructs IdentifiersManager + * @param {!AnP} anp + * @returns {void} + * @access private + * @static + */ + const IdentifiersManager = function(anp){ + + /** @type {IdentifiersManager} */ + const self = this, + /** @type {Array.} */ + identifiers = []; + /** @type {Array.|string} */ + let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + /** @type {number} */ + length = 11; + + /** + * @returns {void} + * @access private + */ + const constructor = () => { + + alphabet = anp.settings.get(["identifiers_alphabet", "default_identifiers_alphabet"], null, alphabet); + length = anp.settings.get(["identifiers_length", "default_identifiers_length"], null, length); + + }; + + /** + * @param {!(string|Array.)} keys + * @param {?(Object.|Array.)} [inputs = null] + * @param {?any} [_default = null] + * @returns {any|null} + * @access public + */ + this.get = () => { + + /** @type {string} */ + let identifier; + + do{ + identifier = ""; + while((identifier += Utils.get_random(alphabet)).length < length); + }while( + identifiers.includes(identifier) || + /^[^a-z]/i.test(identifier) || + document.querySelector("." + identifier + ",#" + identifier + ",[name=" + identifier + "]") + ); + identifiers.push(identifier); + + return identifier; + }; + + /** + * @param {!string} identifier + * @returns {void} + */ + this.remove = identifier => { + identifier in identifiers && identifiers.splice(identifiers.indexOf(identifier), 1); + }; + + constructor(); + + }; + + /** @type {Object.} */ + IdentifiersManager.DEFAULT_SETTINGS = {}; + + return IdentifiersManager; +})(); \ No newline at end of file diff --git a/Public/ecma/Managers/SettingsManager.ecma.js b/Public/ecma/Managers/SettingsManager.ecma.js new file mode 100644 index 0000000..5a332c3 --- /dev/null +++ b/Public/ecma/Managers/SettingsManager.ecma.js @@ -0,0 +1,135 @@ +"use strict"; + +import {Utils} from "../Utils/Utils.ecma.js"; + +/** + * @typedef {import("../Application/AnP.ecma.js").AnP} AnP + */ + +/** + * @class SettingsManager + * @constructor + * @param {!AnP} anp + * @param {?(Object.|Array.)} customs + * @returns {void} + * @access public + * @static + */ +export const SettingsManager = (function(){ + + /** + * @callback settings_manager_default_callback + * @returns {void} + */ + + /** + * @constructs SettingsManager + * @param {!AnP} anp + * @param {?(Object.|Array.)} customs + * @returns {void} + * @access private + * @static + */ + const SettingsManager = function(anp, customs = null){ + + /** @type {SettingsManager} */ + const self = this, + /** @type {Object.} */ + settings = {}, + /** @type {Object.} */ + secrets = {}; + + /** + * @returns {void} + * @access private + */ + const constructor = () => { + + customs = Utils.get_dictionary(customs); + + }; + + /** + * @param {?settings_manager_default_callback} [callback = null] + * @returns {void} + * @access public + */ + this.start = (callback = null) => { + Utils.execute_array([ + "default_settings_files", "settings_files" + ], (key, next_callback) => { + self.add(self.get(key), next_callback, true); + }, () => { + Utils.execute_array([ + "default_secrets_files", "secrets_files" + ], (key, next_callback) => { + self.add_secrets(self.get(key), next_callback, true); + }, callback); + }); + }; + + /** + * @param {!(string|Array.)} keys + * @param {?(Object.|Array.)} [inputs = null] + * @param {?any} [_default = null] + * @returns {any|null} + * @access public + */ + this.get = (keys, inputs = null, _default = null) => ( + Utils.get_value(keys, [inputs, customs, secrets, settings, SettingsManager.DEFAULT_SETTINGS], _default) + ); + + /** + * @param {!(Object.|null>>|Array.|string)} inputs + * @param {?i18n_manager_default_callback} callback + * @param {!boolean} overwrite + * @returns {void} + * @access public + */ + this.add = (inputs, callback = null, overwrite = false) => { + anp.load_json(inputs, (item, next_callback) => { + for(key in item) + (overwrite || Check.is_null_or_undefined(settings[key])) && + (settings[key] = item[key]); + next_callback(); + }, callback, true); + }; + + /** + * @param {!(Object.|Array.|string)} inputs + * @param {?settings_manager_default_callback} [callback = null] + * @param {!boolean} [overwrite = false] + * @returns {void} + * @access public + */ + this.add_secrets = (inputs, callback = null, overwrite = false) => { + anp.load_json(inputs, (item, next_callback) => { + for(const key in item) + (overwrite || Check.is_null_or_undefined(secrets[key])) && + (secrets[key] = item[key]); + next_callback(); + }, callback, true); + }; + + constructor(); + + }; + + /** @type {Object.} */ + SettingsManager.DEFAULT_SETTINGS = { + /** @type {Array.|string|Object.} */ + default_settings_files : ["/json/AnP.settings.json"], + /** @type {Array.|string|Object.} */ + default_secrets_files : ["/json/AnP.secrets.json"], + /** @type {Array.|string|Object.} */ + default_i18n_files : [ + "/json/i18n/AnP.i18n.espanol.json", + "/json/i18n/AnP.i18n.galego.json", + "/json/i18n/AnP.i18n.english.json" + ], + /** @type {Array.|string|Object.} */ + default_database_files : ["/json/database.json"], + }; + + return SettingsManager; +})(); \ No newline at end of file diff --git a/Public/ecma/Utils/Check.ecma.js b/Public/ecma/Utils/Check.ecma.js new file mode 100644 index 0000000..21df70d --- /dev/null +++ b/Public/ecma/Utils/Check.ecma.js @@ -0,0 +1,102 @@ +"use strict"; + +import {RE} from "./Patterns.ecma.js"; + +/** + * @class Check + * @constructor + * @returns {void} + * @access public + * @static + */ +export const Check = (function(){ + + /** + * @constructs Check + * @returns {void} + * @access private + * @static + */ + const Check = function(){}; + + /** + * @param {?any} item + * @returns {boolean} + * @access public + * @static + */ + Check.is_string = item => typeof item == "string"; + + /** + * @param {?any} item + * @returns {boolean} + * @access public + * @static + */ + Check.is_key = item => item && Check.is_string(item) && RE.KEY.test(item); + + /** + * @param {?any} item + * @returns {boolean} + * @access public + * @static + */ + Check.is_array = item => item instanceof Array; + + /** + * @param {?any} item + * @returns {boolean} + * @access public + * @static + */ + Check.is_dictionary = item => item && item.constructor == Object; + + /** + * @param {?any} item + * @returns {boolean} + * @access public + * @static + */ + Check.is_function = item => typeof item == "function"; + + /** + * @param {?any} item + * @returns {boolean} + * @access public + * @static + */ + Check.is_html_item = item => item && (item.tagName || item.nodeName); + + /** + * @param {?any} item + * @returns {boolean} + * @access public + * @static + */ + Check.is_number = item => typeof item == "number"; + + /** + * @param {?any} item + * @returns {boolean} + * @access public + * @static + */ + Check.is_integer = item => Check.is_number(item) && item == item >> 0; + + /** + * @returns {boolean} + * @access public + * @static + */ + Check.is_dark_mode = () => !!window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; + + /** + * @param {?any} item + * @returns {boolean} + * @access public + * @static + */ + Check.is_null_or_undefined = item => item === null || item === undefined; + + return Check; +})(); \ No newline at end of file diff --git a/Public/ecma/Utils/Options.ecma.js b/Public/ecma/Utils/Options.ecma.js new file mode 100644 index 0000000..a29c6d4 --- /dev/null +++ b/Public/ecma/Utils/Options.ecma.js @@ -0,0 +1,113 @@ +"use strict"; + +/** + * @class Options + * @constructor + * @returns {void} + * @access public + * @static + */ +export const Options = (function(){ + + /** + * @constructs Options + * @returns {void} + * @access private + * @static + */ + const Options = function(options, _default = null){ + + /** @type {Options} */ + const self = this; + + /** @type {boolean|null} */ + this.overwrite = null; + /** @type {boolean|null} */ + this.allow_nulls = null; + + /** + * @returns {void} + * @access private + */ + const constructor = () => { + + _default || (_default = Options.DEFAULT); + + [ + ["overwrite", Options.OVERWRITE], + ["allow_nulls", Options.NULLISH] + ].forEach(([key, option]) => { + + /** @type {boolean|null} */ + const value = Options.get_value(options, option); + + self[key] = value !== null ? value : _default[key]; + + }); + + }; + + /** + * @returns {number} + * @access public + */ + this.get = () => { + + /** @type {number} */ + let code = 0; + + [ + [Options.OVERWRITING, self.overwrite], + [Options.NULLISH, self.allow_nulls], + ].forEach(([option, value]) => { + code += ( + value ? 1 : + value === false ? 2 : + 0) * 3 ** option; + }); + + return code; + }; + + constructor(); + + }; + + /** @type {number} */ + Options.OVERWRITING = 0; + /** @type {number} */ + Options.OVERWRITE = 1 + 3 * Options.OVERWRITING; + /** @type {number} */ + Options.NO_OVERWRITE = 2 + 3 * Options.OVERWRITING; + + /** @type {number} */ + Options.NULLISH = 1; + /** @type {number} */ + Options.ALLOW_NULLS = 1 + 3 * Options.NULLISH; + /** @type {number} */ + Options.NOT_NULLS = 2 + 3 * Options.NULLISH; + + /** @type {Options} */ + Options.DEFAULT = new Options(Options.NO_OVERWRITE | Options.ALLOW_NULLS); + + /** + * @param {!number} value + * @param {!number} option + * @returns {boolean|null} + * @access public + * @static + */ + Options.get_value = (value, option) => { + + /** @type {number} */ + const ternary = (value / 3 ** option >> 0) % 3; + + return ( + ternary === 1 ? true : + ternary === 2 ? false : + null + ); + } + + return Options; +})(); \ No newline at end of file diff --git a/Public/ecma/Utils/Patterns.ecma.js b/Public/ecma/Utils/Patterns.ecma.js new file mode 100644 index 0000000..b15161f --- /dev/null +++ b/Public/ecma/Utils/Patterns.ecma.js @@ -0,0 +1,24 @@ +"use strict"; + +/** + * @class RE + * @constructor + * @returns {void} + * @access public + * @static + */ +export const RE = (function(){ + + /** + * @constructs RE + * @returns {void} + * @access private + * @static + */ + const RE = function(){}; + + /** @type {RegExp} */ + RE.KEY = /^[a-z_][a-z0-9_]*$/i; + + return RE; +})(); \ No newline at end of file diff --git a/Public/ecma/Utils/Utils.ecma.js b/Public/ecma/Utils/Utils.ecma.js new file mode 100644 index 0000000..d6a41b5 --- /dev/null +++ b/Public/ecma/Utils/Utils.ecma.js @@ -0,0 +1,404 @@ +"use strict"; + +import {Check} from "./Check.ecma.js"; +import {Options} from "./Options.ecma.js"; + +/** + * @class Utils + * @constructor + * @returns {void} + * @access public + * @static + */ +export const Utils = (function(){ + + /** + * @callback utils_execute_callback + * @param {...(any|null)} inputs + * @returns {any|null} + */ + + /** + * @callback utils_default_callback + * @returns {void} + */ + + /** + * @callback utils_execute_array_each_callback + * @param {?any} item + * @param {!utils_default_callback} callback + * @returns {void} + */ + + /** + * @constructs Utils + * @returns {void} + * @access private + * @static + */ + const Utils = function(){}; + + /** @type {Options} */ + Utils.GET_DICTIONARY_OPTIONS = new Options(Options.NO_OVERWRITE); + /** @type {Options} */ + Utils.GET_VALUE_OPTIONS = new Options(Options.ALLOW_NULLS); + + /** @type {string} */ + Utils.BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + /** @type {string} */ + Utils.RANDOM_ALPHABET = "bHMnuamw/RUBk+xNvCXghsPdlSFG12rLoT0O3VZ=5QeWyI8pADqjcEfJ9Kt64i7Yz"; + + /** + * @param {...(any|null)} items + * @returns {Array} + * @access public + * @static + */ + Utils.get_keys = (...items) => items.reduce((keys, item) => { + + if(Check.is_key(item)) + item in keys || keys.push(item); + else if(Check.is_array(item)) + Utils.get_keys(...item).forEach(key => key in keys || keys.push(key)); + + return keys; + }, []); + + /** + * @param {...(any|null)} items + * @returns {Array.>} + * @access public + * @static + */ + Utils.get_dictionaries = (...items) => items.reduce((dictionaries, item) => { + + if(Check.is_dictionary(item)) + dictionaries.push(item); + else if(Check.is_array(item)) + dictionaries.push(...Utils.get_dictionaries(...item)); + + return dictionaries; + }, []); + + /** + * @param {?any} items + * @param {!number} [custom_options = 0] + * @returns {Object.} + * @access public + * @static + */ + Utils.get_dictionary = (items, custom_options = 0) => { + + /** @type {Object.} */ + const dictionary = {}, + /** @type {Options} */ + options = new Options(custom_options, Utils.GET_DICTIONARY_OPTIONS); + + if(Check.is_dictionary(items)){ + for(const [key, value] of Object.entries(items)) + dictionary[key] = value; + }else if(Check.is_array(items)) + items.forEach(item => { + for(const [key, value] of Object.entries(Utils.get_dictionary(item, options))) + if(options.overwrite || !(key in dictionary)) + dictionary[key] = value; + }); + + return dictionary; + } + + /** + * @param {!(string|Array.)} keys + * @param {!(Object.|Array.)} inputs + * @param {?any} [_default = null] + * @param {!number} [custom_options = 0] + * @returns {any|null} + * @access public + * @static + */ + Utils.get_value = (keys, inputs, _default = null, custom_options = 0) => { + + /** @type {number} */ + const l = (keys = Utils.get_keys(keys)).length, + /** @type {Options} */ + options = new Options(custom_options, Utils.GET_VALUE_OPTIONS); + + if(l){ + + /** @type {number} */ + const m = (inputs = Utils.get_dictionaries(inputs)).length; + + for(let i = 0; i < l; i++) + for(let j = 0; j < m; j++) + if(keys[i] in inputs[j] && (options.allow_nulls || inputs[j][keys[i]] !== null)) + return inputs[j][keys[i]]; + }; + return _default; + }; + + /** + * @param {!string} string + * @param {!(Object.|Array.)} variables + * @param {?any} _default + * @returns {string} + * @access public + * @static + */ + Utils.string_variables = (string, variables, _default = null) => { + + variables = Utils.get_dictionary(variables || {}); + + return ("" + string).replace(/\{([a-z_][a-z0-9_]*)\}/gi, (all, key) => ( + key in variables ? variables[key] : + _default === null ? all : + _default)); + }; + + /** + * @param {!string} string + * @returns {string} + * @access public + * @static + */ + Utils.to_kebab_case = string => ("" + string).replace(/([A-Z]+)|[^a-z0-9]+/g, (_, upper) => ( + upper ? "-" + upper.toLowerCase() : + "-")); + + /** + * @param {!string} string + * @returns {string} + * @access public + * @static + */ + Utils.to_snake_case = string => ("" + string).replace(/([A-Z]+)|[^a-z0-9]+/g, (_, upper) => ( + upper ? "_" + upper.toLowerCase() : + "_")); + + /** + * @param {!HTMLElement} item + * @param {...Object.} attributes + * @returns {void} + * @access public + * @static + */ + Utils.attributes = (item, attributes) => { + for(const [key, value] of Object.entries(attributes)){ + if(/^on[_\-]?/i.test(key) && Check.is_function(value)) + item.addEventListener(key.toLowerCase().replace(/^on[_\-]?/, ""), event => { + Utils.execute(value, item, event); + }); + else + item.setAttribute(Utils.to_kebab_case(key), value); + }; + }; + + /** + * @param {!HTMLElement} item + * @param {...(string|HTMLElement|[string, Object.|null, string|Array.|null]|null)} structure + * @returns {Array.} + * @access public + * @static + */ + Utils.html = (item, ...structure) => { + + /** @type {Array.} */ + const items = []; + + item || (item = document.createDocumentFragment()); + + for(const node of structure) + if(Check.is_string(node)) + item.innerHTML += node; + else if(Check.is_html_item(node)) + items.push(item.appendChild(node)); + else if(Check.is_array(node)){ + + /** @type {[string, Object.|null, Array.|null]} */ + const [tag, attributes, children] = node.concat(null, null).slice(0, 3), + /** @type {HTMLElement} */ + element = document.createElement(tag); + + attributes && Utils.attributes(element, attributes); + if(children && children.length){ + if(Check.is_string(children)) + element.innerHTML += children; + else + Utils.html(element, ...children); + }; + items.push(item.appendChild(element)); + + }; + + return items; + }; + + /** + * @param {?any} item + * @returns {any|null} + * @access public + * @static + */ + Utils.unique = item => ( + Check.is_array(item) ? item.filter((item, i, array) => array.indexOf(item) == i) : + Check.is_string(item) ? item.split("").filter((char, i, array) => array.indexOf(char) == i).join("") : + item); + + /** + * @param {?(number|Array.|string)} [from = null] + * @param {?number} [to = null] + * @returns {any|null} + * @access public + * @static + */ + Utils.get_random = (from = null, to = null) => ( + Check.is_array(from) || Check.is_string(from) ? from.length ? from[Math.random() * from.length >> 0] : null : + Check.is_integer(from) ? ( + to === null ? Math.random() * from >> 0 : + Check.is_integer(to) ? from + (Math.random() * (to - from + 1) >> 0) : + Check.is_number(to) ? from + Math.random() * (to - from) : + null) : + Check.is_number(from) ? ( + to === null ? Math.random() * from : + Check.is_number(to) ? from + Math.random() * (to - from) : + null) : + from === null ? Math.random() : + null); + + /** + * @param {?any} data + * @returns {string} + * @access public + * @static + */ + Utils.encode_data = data => btoa(encodeURIComponent(JSON.stringify(data)).replace(/%([0-9A-F]{2})/g, (_, p1) => String.fromCharCode("0x" + p1))); + + /** + * @param {!string} code + * @param {!boolean} [is_json = true] + * @returns {any|null} + * @access public + * @static + */ + Utils.decode_data = (code, is_json = true) => { + + /** @type {string} */ + const data = decodeURIComponent(atob(code).split("").map(c => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")); + + if(is_json) + try{ + return JSON.parse(data) || data; + }catch(exception){}; + + return data; + }; + + /** + * @param {!utils_execute_callback} callback + * @param {...(any|null)} inputs + * @returns {any|null} + * @access public + * @static + */ + Utils.execute = (callback, ...inputs) => ( + Check.is_function(callback) ? callback(...inputs) : + null); + + /** + * @param {!Array.} array + * @param {!utils_execute_array_each_callback} each_callback + * @param {?utils_default_callback} [end_callback = null] + * @param {!number} [i = 0] + * @returns {void} + * @access public + * @static + */ + Utils.execute_array = (array, each_callback, end_callback = null, i = 0) => { + if(i < array.length) + Utils.execute(each_callback, array[i], () => { + Utils.execute_array(array, each_callback, end_callback, i + 1); + }); + else + Utils.execute(end_callback); + }; + + /** + * @param {?any} item + * @returns {Array.} + * @access public + * @static + */ + Utils.get_array = item => Check.is_array(item) ? item : [item]; + + /** + * @param {!string} string + * @returns {string} + * @access public + * @static + */ + Utils.randomize_string = string => string.split("").sort(() => Utils.get_random() - 0.5).join(""); + + /** + * @param {?any} data + * @returns {string} + * @access public + * @static + */ + Utils.encrypt_data = data => Utils.encode_data(data).split("").map(character => Utils.RANDOM_ALPHABET[Utils.BASE64_ALPHABET.indexOf(character)]).join(""); + + /** + * @param {string} data + * @returns {any|null} + * @access public + * @static + */ + Utils.decrypt_data = data => { + + /** @type {string} */ + const results = Utils.decode_data(data.split("").map(character => Utils.BASE64_ALPHABET[Utils.RANDOM_ALPHABET.indexOf(character)]).join(""), false); + + try{ + return JSON.parse(results) || results; + }catch(exception){}; + + return results; + }; + + /** + * @param {!Array.} array + * @returns {void} + * @access public + * @static + */ + Utils.randomize_array = array => { + + /** @type {number} */ + const l = array.length - 1; + + array.forEach((item, i) => { + + /** @type {number} */ + const j = Utils.get_random(l); + + i != j && ([array[i], array[j]] = [array[j], array[i]]); + + }); + + }; + + /** + * @param {!any} base + * @param {...any} items + * @returns {void} + * @access public + * @static + */ + Utils.extends = (base, ...items) => { + items.forEach(item => { + Object.entries(item).forEach(([key, value]) => { + base[key] = value; + }); + }); + }; + + return Utils; +})(); \ No newline at end of file diff --git a/Public/json/AnP.settings.json b/Public/json/AnP.settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/Public/json/AnP.settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Public/json/i18n/anP.i18n.espanol.json b/Public/json/i18n/anP.i18n.espanol.json new file mode 100644 index 0000000..f464355 --- /dev/null +++ b/Public/json/i18n/anP.i18n.espanol.json @@ -0,0 +1,3 @@ +{ + "espanol" : {} +} \ No newline at end of file diff --git a/Python/Abstracts/BaseAbstract.py b/Python/Abstracts/BaseAbstract.py new file mode 100644 index 0000000..d12c5c3 --- /dev/null +++ b/Python/Abstracts/BaseAbstract.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Self, Callable, Optional +from Interfaces.Application.AnPInterface import AnPInterface + +class BaseAbstract: + + def _build(self:Self) -> None: + pass + + def __init__(self:Self, anp:AnPInterface, key:str) -> None: + self.anp:AnPInterface = anp + self.key:str = key + self._built:bool = False + self._started:bool = False + self._stopped:bool = False + + self._build() + self._built = True + + def _start(self:Self, callback:Optional[Callable[[bool], bool]] = None) -> bool: + pass + + def start(self:Self, callback:Optional[Callable[[bool], bool]] = None) -> bool: + if self._started: + return False if callback is None else callback(False) + self._started = True + + self._stopped = False + return True if callback is None else callback(True) + + def _close(self:Self, callback:Optional[Callable[[bool], bool]] = None) -> bool: + pass + + def close(self:Self, callback:Optional[Callable[[bool], bool]] = None) -> bool: + if self._stopped: + return False if callback is None else callback(False) + self._started = False + + self._close(callback) + + self._stopped = True + return True if callback is None else callback(True) \ No newline at end of file diff --git a/Python/Application/AnP.py b/Python/Application/AnP.py new file mode 100644 index 0000000..50a6f44 --- /dev/null +++ b/Python/Application/AnP.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Self, Any, Optional, Sequence +from Managers.SettingsManager import SettingsManager +from Managers.I18NManager import I18NManager +from Managers.PrintTypesManager import PrintTypesManager + +class AnP: + + def __init__(self:Self, + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None + ) -> None: + self.i18n:I18NManager = I18NManager(self) + self.print_types:PrintTypesManager = PrintTypesManager(self) + self.settings:SettingsManager = SettingsManager(self, inputs) + + def print(self:Self, + _type:str, + data:Any|None, + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None, + i:int = 0 + ) -> None: + + own:dict[str, Any|None] = {} + + def exception(self:Self, + exception:Exception, + message:str|Sequence[str], + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None, + i:int = 0 + ) -> None: + pass \ No newline at end of file diff --git a/Python/Interfaces/Application/AnPInterface.py b/Python/Interfaces/Application/AnPInterface.py new file mode 100644 index 0000000..7125e2f --- /dev/null +++ b/Python/Interfaces/Application/AnPInterface.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Self, Any, Optional, Sequence +from abc import ABC, abstractmethod +from Interfaces.Managers.SettingsManagerInterface import SettingsManagerInterface +from Interfaces.Managers.I18NManagerInterface import I18NManagerInterface +from Interfaces.Managers.PrintTypesManagerInterface import PrintTypesManagerInterface + +class AnPInterface(ABC): + + def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None: + self.print_types:PrintTypesManagerInterface = None + self.i18n:I18NManagerInterface = None + self.settings:SettingsManagerInterface = None + + @abstractmethod + def print(self:Self, + _type:str, + data:Any|None, + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None, + i:int = 0 + ) -> None:pass + + @abstractmethod + def exception(self:Self, + exception:Exception, + message:str|Sequence[str], + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None, + i:int = 0 + ) -> None:pass \ No newline at end of file diff --git a/Python/Interfaces/Managers/I18NManagerInterface.py b/Python/Interfaces/Managers/I18NManagerInterface.py new file mode 100644 index 0000000..da78993 --- /dev/null +++ b/Python/Interfaces/Managers/I18NManagerInterface.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Any, Self, Sequence, Optional +from abc import ABC, abstractmethod + +class I18NManagerInterface(ABC): + + @abstractmethod + def get(self:Self, + strings:str|Sequence[str], + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None, + languages:Optional[str|Sequence[str]] = None, + custom_options:int = 0 + ) -> Any|None:pass + + @abstractmethod + def add(self:Self, inputs:Any|None, custom_options:int = 0) -> None:pass \ No newline at end of file diff --git a/Python/Interfaces/Managers/PrintTypesManagerInterface.py b/Python/Interfaces/Managers/PrintTypesManagerInterface.py new file mode 100644 index 0000000..8370de9 --- /dev/null +++ b/Python/Interfaces/Managers/PrintTypesManagerInterface.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Any, Self +from abc import ABC, abstractmethod + +class PrintTypesManagerInterface(ABC): + + @abstractmethod + def get(self:Self, _type:str) -> str:pass + + @abstractmethod + def add(self:Self, inputs:Any|None, custom_options:int = 0) -> None:pass diff --git a/Python/Interfaces/Managers/SettingsManagerInterface.py b/Python/Interfaces/Managers/SettingsManagerInterface.py new file mode 100644 index 0000000..2f3309a --- /dev/null +++ b/Python/Interfaces/Managers/SettingsManagerInterface.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Any, Self, Sequence, Optional +from abc import ABC, abstractmethod + +class SettingsManagerInterface(ABC): + + @abstractmethod + def get(self:Self, + keys:str|Sequence[str], + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None, + default:Any|None = None, + custom_options:int = 0 + ) -> Any|None:pass + + @abstractmethod + def add(self:Self, inputs:Any|None, custom_options:int = 0) -> None:pass \ No newline at end of file diff --git a/Python/Managers/I18NManager.py b/Python/Managers/I18NManager.py new file mode 100644 index 0000000..d306dce --- /dev/null +++ b/Python/Managers/I18NManager.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Any, Self, Sequence, Optional +from Interfaces.Application.AnPInterface import AnPInterface +from Utils.Utils import Utils +from Utils.Options import Options + +class I18NManager: + + DEFAULT_SENTENCES:dict[str, Any|None] = { + "english" : {} + } + + GET_OPTIONS:Options = Options(Options.ALLOW_NULLS) + ADD_OPTIONS:Options = Options(Options.NO_OVERWRITE) + + def __init__(self:Self, anp:AnPInterface) -> None: + self.anp:AnPInterface = anp + + self.__sentences:dict[str, Any|None] = self.DEFAULT_SENTENCES + self.__language:str = "english" + self.__default_language:str = "english" + + def __get_sentence(self:Self, + strings:str|Sequence[str], + languages:list[str], + options:Options + ) -> str|list[str]|None: + + keys:list[str] = Utils.get_keys(strings) + + if len(keys): + + languages_used:list[str] = [] + language:str + + for language in languages + [ + self.__language, self.__default_language + ] + list(self.__sentences.keys()): + if language in languages_used: + continue + + key:str + + languages_used.append(language) + + for key in keys: + if key in self.__sentences[language] and ( + options.allow_null or + self.__sentences[language][key] is not None + ): + return self.__sentences[language][key] + return Utils.get_strings(strings)[0] + + def get(self:Self, + strings:str|Sequence[str], + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None, + languages:Optional[str|Sequence[str]] = None, + custom_options:int = 0 + ) -> Any|None: + + options:Options = Options(custom_options, self.GET_OPTIONS) + text:str|list[str]|None = self.__get_sentence(strings, Utils.get_array(languages), options) + + return Utils.string_variables(( + text if isinstance(text, str) else + "".join(text) if isinstance(text, (list, tuple)) else + str(text)), inputs) + + def add(self:Self, inputs:Any|None, custom_options:int = 0) -> None: + pass \ No newline at end of file diff --git a/Python/Managers/PrintTypesManager.py b/Python/Managers/PrintTypesManager.py new file mode 100644 index 0000000..306eb80 --- /dev/null +++ b/Python/Managers/PrintTypesManager.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Any, Self +from Interfaces.Application.AnPInterface import AnPInterface +from Models.PrintTypeModel import PrintTypeModel + +class PrintTypesManager: + + def __init__(self:Self, anp:AnPInterface) -> None: + self.anp:AnPInterface = anp + self.__print_types:list[PrintTypeModel] = [ + PrintTypeModel(["unkn", "unknown"], "#888") + ] + + def get(self:Self, _type:str) -> str: + + subset:list[str] + + for subset in self.__print_types: + if _type in subset: + return subset[0] + return self.__print_types[0][0] + + def add(self:Self, inputs:Any|None, custom_options:int = 0) -> None: + pass diff --git a/Python/Managers/SettingsManager.py b/Python/Managers/SettingsManager.py new file mode 100644 index 0000000..faf5af1 --- /dev/null +++ b/Python/Managers/SettingsManager.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Any, Self, Sequence, Optional +from Interfaces.Application.AnPInterface import AnPInterface +from Utils.Utils import Utils +from Utils.Options import Options + +class SettingsManager: + + DEFAUTL_SETTINGS:dict[str, Any|None] = {} + + GET_OPTIONS:Options = Options(Options.ALLOW_NULLS) + ADD_OPTIONS:Options = Options(Options.NO_OVERWRITE) + + def __init__(self:Self, + anp:AnPInterface, + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None + ) -> None: + self.anp:AnPInterface = anp + + self.__inputs:dict[str, Any|None] = Utils.get_dictionary(inputs, Options.OVERWRITE) + self.__secrets:dict[str, Any|None] = {} + + def get(self:Self, + keys:str|Sequence[str], + inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None, + default:Any|None = None, + custom_options:int = 0 + ) -> Any|None: + return Utils.get_value(keys, ( + inputs, self.__inputs, self.__secrets, self.DEFAUTL_SETTINGS + ), default, Options(custom_options, self.GET_OPTIONS).get()) + + def add(self:Self, inputs:Any|None, custom_options:int = 0) -> None: + pass \ No newline at end of file diff --git a/Python/Models/PrintTypeModel.py b/Python/Models/PrintTypeModel.py new file mode 100644 index 0000000..4a27aa1 --- /dev/null +++ b/Python/Models/PrintTypeModel.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Optional, Self, Sequence +from Utils.Color import Color +from Utils.Utils import Utils + +class PrintTypeModel: + + def __init__(self:Self, + names:str|Sequence[str], + fore:str|int|Sequence[int]|Color, + back:Optional[str|int|Sequence[int]|Color] = None + ) -> None: + + self.names:list[str] = [] + self.name:str + self.fore:Color = Color(fore) + self.back:Color|None = None if back is None else Color(back) + + self.add_names(names) + self.name = self.names[0].upper() + + def add_names(self:Self, names:str|Sequence[str]) -> None: + + name:str + + for name in Utils.get_keys(names, self.names): + if name not in self.names: + self.names.append(name) \ No newline at end of file diff --git a/Python/Utils/Check.py b/Python/Utils/Check.py new file mode 100644 index 0000000..90d247d --- /dev/null +++ b/Python/Utils/Check.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Any, Self +from Utils.Patterns import RE + +class Check: + + @staticmethod + def is_string(value:Any|None) -> bool: + return isinstance(value, str) + + @classmethod + def is_key(cls:type[Self], value:Any|None) -> bool: + return cls.is_string(value) and RE.KEY.match(value) is not None + + @staticmethod + def is_array(value:Any|None) -> bool: + return isinstance(value, (list, tuple)) + + @staticmethod + def is_dictionary(value:Any|None) -> bool: + return isinstance(value, dict) + + @staticmethod + def is_integer(value:Any|None) -> bool: + return isinstance(value, int) \ No newline at end of file diff --git a/Python/Utils/Color.py b/Python/Utils/Color.py new file mode 100644 index 0000000..05e75b6 --- /dev/null +++ b/Python/Utils/Color.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Self, Sequence +from Utils.Check import Check + +class Color: + + def __init__(self:Self, inputs:str|int|Sequence[int]|Self, _type:str = "rgba") -> None: + + self.red:int = 0x00 + self.green:int = 0x00 + self.blue:int = 0x00 + self.alpha:int = 0xFF + + if Check.is_string(inputs): + if inputs.startswith("#"): + inputs = inputs[1:] + if len(inputs) in (3, 4): + inputs = "".join([character * 2 for character in inputs]) + if len(inputs) in (7, 9): + inputs = ("FF" + inputs[1:])[-8:] + self.alpha, self.red, self.green, self.blue = [int(inputs[i:i + 2], 16) for i in (0, 2, 4, 6)] + elif inputs.startswith("rgb"): + self.red, self.green, self.blue, self.alpha = ([( + int(item) if i < 3 else + int(float(item) * 0xFF)) for item, i in enumerate(inputs[inputs.find("(") + 1:inputs.find(")")].split(","))] + [0xFF])[:4] + elif Check.is_integer(inputs): + self.alpha = 0xFF - ((inputs >> 24) & 0xFF) + self.red = (inputs >> 16) & 0xFF + self.green = (inputs >> 8) & 0xFF + self.blue = inputs & 0xFF + elif Check.is_array(inputs): + if _type in ("rgb", "rgba") or True: + self.red, self.green, self.blue, self.alpha = (inputs + [0xFF] * 4)[:4] + elif isinstance(inputs, Color): + self.red = inputs.red + self.green = inputs.green + self.blue = inputs.blue + self.alpha = inputs.alpha + + def to_rgba(self:Self) -> str: + return f"rgba({self.red}, {self.green}, {self.blue}, {self.alpha / 0xFF:.2f})" + + def to_rgb(self:Self) -> str: + return f"rgb({self.red}, {self.green}, {self.blue})" + + def to_hex(self:Self, with_alpha:bool = False) -> str: + return "#" + (f"{self.alpha:02X}" if with_alpha else "") + f"{self.red:02X}{self.green:02X}{self.blue:02X}" + + @staticmethod + def mix(*colors:str|int|Sequence[int]|Self) -> Self: + if len(colors) == 0: + return Color(0) + + base:Color = Color(colors[0]) + color:str|int|Sequence[int]|Color + l:int = len(colors) + + if l != 1: + for color in colors[1:]: + + subcolor:Color = Color(color) + + base.red += subcolor.red + base.green += subcolor.green + base.blue += subcolor.blue + base.alpha += subcolor.alpha + + base.red //= l + base.green //= l + base.blue //= l + base.alpha //= l + + return base \ No newline at end of file diff --git a/Python/Utils/Options.py b/Python/Utils/Options.py new file mode 100644 index 0000000..78d685d --- /dev/null +++ b/Python/Utils/Options.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Self, Optional + +class Options: + + OVERWRITING:int = 0 + OVERWRITE:int = 1 * 3 ** OVERWRITING + NO_OVERWRITE:int = 2 * 3 ** OVERWRITING + + NULLISH:int = 1 + ALLOW_NULLS:int = 1 * 3 ** NULLISH + NOT_NULLS:int = 2 * 3 ** NULLISH + + DEFAULT:Self = Self(NO_OVERWRITE + ALLOW_NULLS) + + @classmethod + def get_value(cls:type[Self], value:int, option:int) -> bool|None: + + ternary:int = value // 3 ** option % 3 + + return ( + True if ternary == 1 else + False if ternary == 2 else + None) + + def __init__(self:Self, options:int = 0, default:Optional[Self] = None) -> None: + + self.overwrite:bool|None + self.allow_null:bool|None + + if default is None: + default = self.DEFAULT + + for key, option in ( + ("overwrite", self.OVERWRITE), + ("allow_null", self.NULLISH) + ): + + value:bool|None = self.get_value(options, option) + + setattr(self, key, getattr(default, key) if value is None and default is not None else value) + + def get(self:Self) -> int: + + code:int = 0 + option:int + value:bool|None + + for option, value in ( + (self.OVERWRITING, self.overwrite), + (self.NULLISH, self.allow_null) + ): + code += ( + 1 if value is True else + 2 if value is False else + 0) * 3 ** option + + return code \ No newline at end of file diff --git a/Python/Utils/Patterns.py b/Python/Utils/Patterns.py new file mode 100644 index 0000000..1bfd4a7 --- /dev/null +++ b/Python/Utils/Patterns.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from re import compile as re_compile, Pattern as REPattern, I as RE_IGNORE_CASE + +class RE: + KEY:REPattern = re_compile(r'^[a-z_][a-z0-9_]*$', RE_IGNORE_CASE) + STRING_VARIABLES:REPattern = re_compile(r'\{([a-z_][a-z0-9_]*)\}', RE_IGNORE_CASE) \ No newline at end of file diff --git a/Python/Utils/Utils.py b/Python/Utils/Utils.py new file mode 100644 index 0000000..925d79e --- /dev/null +++ b/Python/Utils/Utils.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from typing import Any, Self, Sequence, Optional +from re import Match as REMatch +from Utils.Check import Check +from Utils.Options import Options +from Utils.Patterns import RE + +class Utils: + + GET_DICTIONARY_OPTIONS:Options = Options(Options.NO_OVERWRITE) + GET_VALUE_OPTIONS:Options = Options(Options.ALLOW_NULLS) + + @classmethod + def get_keys(cls:type[Self], *items:Any|None) -> list[str]: + + keys:list[str] = [] + item:Any|None + + for item in items: + if Check.is_key(item): + item in keys or keys.append(item) + elif Check.is_array(item): + + key:str + + for key in cls.get_keys(*item): + key in keys or keys.append(key) + + return keys + + @classmethod + def get_dictionaries(cls:type[Self], *items:Any|None) -> list[dict[str, Any|None]]: + + dictionaries:list[dict[str, Any|None]] = [] + item:Any|None + + for item in items: + if Check.is_dictionary(item): + item in dictionaries or dictionaries.append(item) + elif Check.is_array(item): + + dictionary:dict + + for dictionary in cls.get_dictionaries(*item): + dictionary in dictionaries or dictionaries.append(dictionary) + + return dictionaries + + @classmethod + def get_dictionary(cls:type[Self], items:Any|None, custom_options:int = 0) -> dict[str, Any|None]: + + dictionary:dict[str, Any|None] = {} + + options:Options = Options(custom_options, cls.GET_DICTIONARY_OPTIONS) + + if Check.is_dictionary(items): + dictionary = items + elif Check.is_array(items): + + item:Any|None + + for item in items: + + key:str + value:Any|None + + for key, value in cls.get_dictionary(item, options).items(): + if options.overwrite or key not in dictionary: + dictionary[key] = value + + return dictionary + + @classmethod + def get_value(cls:type[Self], + keys:str|Sequence[str], + inputs:dict[str, Any|None]|Sequence[Any|None], + default:Optional[Any] = None, + custom_options:int = 0 + ) -> Any|None: + + options:Options = Options(custom_options, cls.GET_VALUE_OPTIONS) + + if len(keys := cls.get_keys(keys)): + + subinputs:dict[str, Any|None] + + for subinputs in cls.get_dictionaries(inputs): + + key:str + + for key in keys: + if key in subinputs and ( + options.allow_null or + subinputs[key] is not None + ): + return subinputs[key] + return default + + @staticmethod + def get_array(value:Any|None) -> list[Any]: + return ( + value if isinstance(value, list) else + list(value) if isinstance(value, (set, tuple)) else + [value]) + + @staticmethod + def get_strings(value:Any|None) -> list[str]: + + strings:list[str] = [] + item:Any|None + + for item in Utils.get_array(value): + if Check.is_string(item): + item in strings or strings.append(item) + elif Check.is_array(item): + strings.extend(Utils.get_strings(item)) + + return strings + + @classmethod + def string_variables(cls:type[Self], + string:str, + variables:dict[str, Any|None], + default:Optional[str] = None + ) -> str: + + variables = Utils.get_dictionary(variables) + + def callback(matches:REMatch) -> str: + + key:str = matches.group(1) + + return ( + str(variables[key]) if key in variables else + default if default is not None else + matches.group(0)) + + return RE.STRING_VARIABLES.sub(callback, string) \ No newline at end of file diff --git a/README.md b/README.md index a98d843..384bc13 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,39 @@ # AnPv2 -AnP Framework, version 2. \ No newline at end of file +AnP Framework, version 2. + +## .NET + +- Docker Hub del SDK: https://hub.docker.com/r/microsoft/dotnet-sdk +- Git del SDK: https://github.com/dotnet/sdk + +Para crear los SLN de la Solución: + +```sh +#!/bin/bash + +docker exec -it anp-dotnet bash + +cd CSharp +dotnet new sln -n AnP +dotnet sln add AnP.csproj +cd .. + +exit + +sudo chown -R root:$USER CSharp/AnP.slnx + +``` + +Instalación del SDK para desarrollo VSCode. + +```sh +#!/bin/bash +wget https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb +sudo dpkg -i packages-microsoft-prod.deb +rm packages-microsoft-prod.deb +sudo apt update +sudo apt install -y dotnet-sdk-10.0 +``` + +Luego, instalar el Pluggin Nuget de Visual Studio Code `C/C++ DevTools`. \ No newline at end of file diff --git a/SQLServer/AnP.1.10.common.server.sql b/SQLServer/AnP.1.10.common.server.sql new file mode 100644 index 0000000..c00e878 --- /dev/null +++ b/SQLServer/AnP.1.10.common.server.sql @@ -0,0 +1,690 @@ +if (select top 1 0 from sys.databases where name = 'AnP') is null create database AnP collate Latin1_General_CI_AS +go +use AnP + +if object_id(N'dbo.common_tables_delete', N'P') is not null drop procedure dbo.common_tables_delete +go +create procedure dbo.common_tables_delete as begin + + set nocount on + + if object_id(N'dbo.fk_sessions_user', N'F') is not null + alter table dbo.[Sessions] drop constraint fk_sessions_user + + -- Level 3. + if object_id(N'dbo.Logs', N'U') is not null drop table dbo.Logs + if object_id(N'dbo.Exceptions', N'U') is not null drop table dbo.Exceptions + if object_id(N'dbo.Terminals', N'U') is not null drop table dbo.Terminals + if object_id(N'dbo.SessionsIps', N'U') is not null drop table dbo.SessionsIps + + -- Level 2. + if object_id(N'dbo.[Sessions]', N'U') is not null drop table dbo.[Sessions] + + -- Level 1. + if object_id(N'dbo.[Procedures]', N'U') is not null drop table dbo.[Procedures] + if object_id(N'dbo.Users', N'U') is not null drop table dbo.Users + + -- Level 0. + if object_id(N'dbo.Ips', N'U') is not null drop table dbo.Ips + if object_id(N'dbo.[Databases]', N'U') is not null drop table dbo.[Databases] + if object_id(N'dbo.Hashes', N'U') is not null drop table dbo.Hashes + if object_id(N'dbo.[Messages]', N'U') is not null drop table dbo.[Messages] + if object_id(N'dbo.Errors', N'U') is not null drop table dbo.Errors + + -- Level historical. + if object_id(N'dbo.UsersData', N'U') is not null drop table dbo.UsersData + +end +go + +if object_id(N'dbo.common_tables_create', N'P') is not null drop procedure dbo.common_tables_create +go +create procedure dbo.common_tables_create as begin + + set nocount on + + -- Level historical. + if object_id(N'dbo.UsersData', N'U') is null create table dbo.UsersData( + id integer not null identity(1, 1), + entity integer not null, + [procedure] integer not null, + [session] integer not null, + [hash] integer, + nick varchar(32) collate database_default, + [password] binary(64), + accessible bit, + date_in datetime not null, + date_out datetime, + constraint pk_users_data primary key clustered (id) + ) + + -- Level 0. + if object_id(N'dbo.[Databases]', N'U') is null create table dbo.[Databases]( + id integer not null identity(1, 1), + [name] varchar(64) collate database_default not null, + date_in datetime not null constraint df_databases_date_in default getdate(), + date_out datetime, + constraint pk_databases primary key clustered (id), + constraint uk_databases_name unique nonclustered ([name] asc) with (fillfactor = 90), + constraint ck_databases_name check ( + [name] != '' and + [name] like '[a-z_]%' and + [name] not like '%[^a-z0-9_]%' + ) + ) + + if object_id(N'dbo.[Messages]', N'U') is null create table dbo.[Messages]( + id integer not null identity(1, 1), + [message] varchar(max) collate database_default not null, + date_in datetime not null constraint df_messages_date_in default getdate(), + date_out datetime, + constraint pk_messages primary key clustered (id) + ) + + if object_id(N'dbo.Hashes', N'U') is null create table dbo.Hashes( + id integer not null identity(1, 1), + [name] varchar(16) collate database_default not null, + date_in datetime not null constraint df_hashes_date_in default getdate(), + date_out datetime, + constraint pk_hashes primary key clustered (id), + constraint uk_hashes_name unique nonclustered ([name] asc) with (fillfactor = 90), + constraint ck_hashes_name check ([name] != '') + ) + + if object_id(N'dbo.Errors', N'U') is null create table dbo.Errors( + id integer not null identity(1, 1), + [error] varchar(512) collate database_default not null, + date_in datetime not null constraint df_errors_date_in default getdate(), + date_out datetime, + constraint pk_errors primary key clustered (id), + constraint uk_errors_error unique nonclustered ([error] asc) with (fillfactor = 90) + ) + + create table dbo.Ips( + id integer not null identity(1, 1), + [ip] varchar(45) collate database_default not null, -- 3x4+3=15 for IPv4; 8x4+7=39 for IPv6; 30+15 for Hibrid. + date_in datetime not null constraint df_ips_date_in default getdate(), + date_out datetime, + constraint pk_ips primary key clustered (id), + constraint uk_ips_ip unique nonclustered ([ip] asc) with (fillfactor = 90), + constraint ck_ips_ip check (len([ip]) > 1 and [ip] not like '%[^0-9a-f:.]%'), + index ix_ips_ip nonclustered ([ip] asc) with (fillfactor = 90) + ) + + -- Level 1. + if object_id(N'dbo.[Procedures]', N'U') is null begin + create table dbo.[Procedures]( + id integer not null identity(1, 1), + [database] integer not null, + [name] varchar(64) collate database_default not null, + date_in datetime not null constraint df_procedures_date_in default getdate(), + date_out datetime, + constraint pk_procedures primary key clustered (id), + constraint fk_procedures_database foreign key([database]) references dbo.[Databases](id), + constraint uk_procedures_name unique([database], [name]), + constraint ck_procedures_database check ([database] > 0), + constraint ck_procedures_name check ( + [name] != '' and + [name] like '[a-z_]%' and + [name] not like '%[^a-z0-9_]%' + ) + ) + create nonclustered index ix_procedures_name on dbo.[Procedures]([name] asc) include ([database]) with (fillfactor = 90) + end + + if object_id(N'dbo.Users', N'U') is null create table dbo.Users( + id integer not null identity(1, 1), + [hash] integer not null constraint df_users_hash default 1, + nick varchar(32) collate database_default not null, + [password] binary(64), + accessible bit not null constraint df_users_accessible default 1, + date_in datetime not null constraint df_users_date_in default getdate(), + date_out datetime, + constraint pk_users_id primary key clustered (id), + constraint fk_users_hash foreign key([hash]) references dbo.Hashes(id), + constraint uk_users_nick unique nonclustered (nick asc) with (fillfactor = 90), + constraint ck_users_hash check ([hash] > 0), + constraint ck_users_nick check (nick != ''), + index ix_users_hash nonclustered ([hash] asc) with (fillfactor = 90), + index ix_users_nick nonclustered (nick asc) with (fillfactor = 90) + ) + + -- level 2. + if object_id(N'dbo.[Sessions]', N'U') is null create table dbo.[Sessions]( + id integer not null identity(1, 1), + [user] integer not null, + date_last datetime not null constraint df_sessions_date_last default getdate(), + date_in datetime not null constraint df_sessions_date_in default getdate(), + date_out datetime, + constraint pk_sessions primary key clustered (id), + constraint fk_sessions_user foreign key([user]) references dbo.Users(id), + constraint ck_sessions_user check ([user] > 0), + index ix_sessions_user nonclustered ([user] asc) with (fillfactor = 90) + ) + + -- Level 3. + if object_id(N'dbo.Logs', N'U') is null begin + create table dbo.Logs( + id integer not null identity(1, 1), + [procedure] integer not null, + [session] integer not null, + [message] integer not null, + error integer, + parameters text, + date_in datetime not null constraint df_logs_date_in default getdate(), + date_out datetime, + constraint pk_logs primary key clustered (id), + constraint fk_logs_procedure foreign key([procedure]) references dbo.[Procedures](id), + constraint fk_logs_session foreign key([session]) references dbo.[Sessions](id), + constraint fk_logs_message foreign key([message]) references dbo.[Messages](id), + constraint fk_logs_error foreign key(error) references dbo.Errors(id), + constraint ck_logs_procedure check ([procedure] > 0), + constraint ck_logs_session check ([session] > 0), + constraint ck_logs_message check ([message] > 0), + constraint ck_logs_error check (error is null or error > 0) + ) + create nonclustered index ix_logs_procedure on dbo.Logs ([procedure] asc) include ([session], [message], error) with (fillfactor = 90) + create nonclustered index ix_logs_session on dbo.Logs ([session] asc) include ([procedure], [message], error) with (fillfactor = 90) + create nonclustered index ix_logs_error on dbo.Logs (error asc) include ([procedure], [session], [message]) with (fillfactor = 90) + create nonclustered index ix_logs_message on dbo.Logs ([message] asc) include ([procedure], [session], error) with (fillfactor = 90) + end + + if object_id(N'dbo.Exceptions', N'U') is null begin + create table dbo.Exceptions( + id integer not null identity(1, 1), + [procedure] integer not null, + [session] integer not null, + [message] integer not null, + exception integer not null, + parameters text, + [status] varchar(16) collate database_default, + code integer, + date_in datetime not null constraint df_exceptions_date_in default getdate(), + date_out datetime, + constraint pk_exceptions primary key clustered (id), + constraint fk_exceptions_procedure foreign key([procedure]) references dbo.[Procedures](id), + constraint fk_exceptions_session foreign key([session]) references dbo.[Sessions](id), + constraint fk_exceptions_message foreign key([message]) references dbo.[Messages](id), + constraint fk_exceptions_exception foreign key([exception]) references dbo.[Messages](id), + constraint ck_exceptions_procedure check ([procedure] > 0), + constraint ck_exceptions_session check ([session] > 0), + constraint ck_exceptions_message check ([message] > 0), + constraint ck_exceptions_exception check ([exception] > 0) + ) + create nonclustered index ix_exceptions_procedure on dbo.Exceptions ([procedure] asc) include ([session], [message], exception) with (fillfactor = 90) + create nonclustered index ix_exceptions_session on dbo.Exceptions ([session] asc) include ([procedure], [message], exception) with (fillfactor = 90) + create nonclustered index ix_exceptions_exception on dbo.Exceptions (exception asc) include ([procedure], [session], [message]) with (fillfactor = 90) + create nonclustered index ix_exceptions_message on dbo.Exceptions ([message] asc) include ([procedure], [session], exception) with (fillfactor = 90) + end + + if object_id(N'dbo.Terminals', N'U') is null begin + create table dbo.Terminals( + id integer not null identity(1, 1), + [procedure] integer not null, + [session] integer not null, + [message] varchar(max) collate database_default, + date_in datetime not null constraint df_terminals_date_in default getdate(), + date_out datetime, + constraint pk_terminals_id primary key clustered (id), + constraint fk_terminals_procedure foreign key([procedure]) references dbo.[Procedures](id), + constraint fk_terminals_session foreign key([session]) references dbo.[Sessions](id), + constraint ck_terminals_procedure check ([procedure] > 0), + constraint ck_terminals_session check ([session] > 0) + ) + create nonclustered index ix_terminals_procedure on dbo.Terminals ([procedure] asc) include ([session]) with (fillfactor = 90) + create nonclustered index ix_terminals_session on dbo.Terminals ([session] asc) include ([procedure]) with (fillfactor = 90) + end + + if object_id(N'dbo.SessionsIps', N'U') is null begin + create table dbo.SessionsIps( + id integer not null identity(1, 1), + [session] integer not null, + [ip] integer not null, + date_in datetime not null constraint df_sessions_ips_date_in default getdate(), + date_out datetime, + constraint pk_sessions_ips_id primary key clustered (id), + constraint fk_sessions_ips_session foreign key([session]) references dbo.[Sessions](id), + constraint fk_sessions_ips_ip foreign key([ip]) references dbo.Ips(id), + constraint ck_sessions_ips_session check ([session] > 0), + constraint ck_sessions_ips_ip check ([ip] > 0) + ) + create nonclustered index ix_sessions_ips_session on dbo.SessionsIps ([session] asc) include ([ip]) with (fillfactor = 90) + create nonclustered index ix_sessions_ips_ip on dbo.SessionsIps ([ip] asc) include ([session]) with (fillfactor = 90) + end + +end +go + +if object_id(N'dbo.common_tables_update', N'P') is not null drop procedure dbo.common_tables_update +go +create procedure dbo.common_tables_update as begin + + set nocount on + +end +go + +execute dbo.common_tables_delete +go +execute dbo.common_tables_create +go +execute dbo.common_tables_update +go + +if object_id(N'dbo.sql_set', N'P') is not null drop procedure dbo.sql_set +go +create procedure dbo.sql_set + @database varchar(64), + @procedure varchar(64), + @session integer +as begin + + declare @data binary(128) = ( + cast(substring(isnull(@database, ''), 1, 62) as binary(62)) + + cast(substring(isnull(@procedure, ''), 1, 62) as binary(62)) + + cast(isnull(@session, 0) as binary(4)) + ) + + set nocount on + + set context_info @data + +end +go + +if object_id(N'dbo.sql_get', N'P') is not null drop procedure dbo.sql_get +go +create procedure dbo.sql_get + @database varchar(64) output, + @procedure varchar(64) output, + @session integer output +as begin + + declare @context binary(128) = context_info() + declare @null char(1) = char(0) + + set nocount on + + set @database = rtrim(replace(cast(substring(@context, 1, 62) as varchar(64)), @null, '')) + set @procedure = rtrim(replace(cast(substring(@context, 63, 62) as varchar(64)), @null, '')) + set @session = cast(substring(@context, 125, 4) as int) + +end +go + +if object_id(N'dbo.procedure_get', N'P') is not null drop procedure dbo.procedure_get +go +create procedure dbo.procedure_get + @database varchar(64), + @procedure varchar(64), + @id integer output +as begin + + declare @database_id integer = (select top 1 id from dbo.[Databases] where + date_out is null and + [name] = @database + ) + + set nocount on + + if @database_id is null begin + insert into dbo.[Databases]([name]) values(@database) + set @database_id = scope_identity() + end + + set @id = (select top 1 id from dbo.[Procedures] where + date_out is null and + [database] = @database_id and + [name] = @procedure + ) + + if @id is null begin + insert into dbo.[Procedures]([database], [name]) values(@database_id, @procedure) + set @id = scope_identity() + end + +end +go + +if object_id(N'dbo.users_trigger_update', N'TR') is not null drop trigger dbo.users_trigger_update +go +create trigger dbo.users_trigger_update on dbo.Users after insert, update as begin + + declare @session integer + declare @database varchar(64) + declare @procedure varchar(64) + declare @procedure_id integer + declare @id integer + declare @hash integer + declare @nick varchar(32) + declare @password binary(64) + declare @accessible bit + declare @date_in datetime + declare @date_out datetime + declare users_cursor cursor local for ( + select id, [hash], nick, [password], accessible, date_in, date_out from inserted + ) + + set nocount on + + execute dbo.sql_get @database output, @procedure output, @session output + execute dbo.procedure_get @database, @procedure, @procedure_id output + + open users_cursor + while 1 = 1 begin + fetch next from users_cursor into @id, @hash, @nick, @password, @accessible, @date_in, @date_out + if @@fetch_status != 0 + break + insert into dbo.UsersData(entity, [procedure], [session], [hash], nick, [password], accessible, date_in, date_out) values + (@id, @procedure_id, @session, @hash, @nick, @password, @accessible, @date_in, @date_out) + end + close users_cursor + deallocate users_cursor + +end +go + +if object_id(N'dbo.hash_create_name', N'FN') is not null drop function dbo.hash_create_name +go +create function dbo.hash_create_name( + @method varchar(16) +) returns varchar(16) as begin + return (case + when @method = 'md5' then 'md5' + when @method = 'sha1' then 'sha1' + when @method in ( + 'sha2_256', 'sha2-256', 'sha-256', 'sha256', 'sha_256' + ) then 'sha2_256' + when @method in ( + 'sha2_512', 'sha2-512', 'sha-512', 'sha512', 'sha_512' + ) then 'sha2_512' + else null end) +end +go + +if object_id(N'dbo.hash_create', N'FN') is not null drop function dbo.hash_create +go +create function dbo.hash_create( + @method varchar(16), + @input varchar(max) +) returns binary(64) as begin + + set @method = dbo.hash_create_name(@method) + + return (case + when @method in ( + 'md5', 'sha1', 'sha2_256', 'sha2_512' + ) then hashbytes(upper(@method), @input) + else cast(@input as binary(64)) end) +end +go + +if object_id(N'dbo.message_get', N'P') is not null drop procedure dbo.message_get +go +create procedure dbo.message_get + @message varchar(max), + @id integer output +as begin + + set nocount on + + set @id = (select top 1 id from dbo.[Messages] where date_out is null and [message] = @message) + + if @id is null begin + insert into dbo.[Messages]([message]) values(@message) + set @id = scope_identity() + end + +end +go + +if object_id(N'dbo.error_get', N'P') is not null drop procedure dbo.error_get +go +create procedure dbo.error_get + @error varchar(512), + @id integer output +as begin + + set nocount on + + set @id = (select top 1 id from dbo.Errors where date_out is null and [error] = @error) + + if @id is null begin + insert into dbo.Errors([error]) values(@error) + set @id = scope_identity() + end + +end +go + +if object_id(N'dbo.hash_get', N'P') is not null drop procedure dbo.hash_get +go +create procedure dbo.hash_get + @name varchar(16), + @id integer output +as begin + + set nocount on + + set @id = (select top 1 id from dbo.Hashes where date_out is null and [name] = @name) + + if @id is null begin + insert into dbo.Hashes([name]) values(@name) + set @id = scope_identity() + end + +end +go + +if object_id(N'dbo.logs_set', N'P') is not null drop procedure dbo.logs_set +go +create procedure dbo.logs_set + @session integer, + @database varchar(64), + @procedure varchar(64), + @message varchar(max), + @parameters varchar(max), + @error varchar(512) = '', + @level tinyint = 0 +as begin + + declare @procedure_id integer + declare @error_id integer + declare @message_id integer + + set nocount on + + execute dbo.procedure_get @database, @procedure, @procedure_id output + execute dbo.message_get @message, @message_id output + execute dbo.error_get @error, @error_id output + + insert into dbo.Logs([procedure], [session], [message], parameters, [error]) values + (@procedure_id, @session, @message_id, @parameters, @error_id) + +end +go + +if object_id(N'dbo.logs_set', N'P') is not null drop procedure dbo.logs_set +go +create procedure dbo.logs_set + @session integer, + @database varchar(64), + @procedure varchar(64), + @message varchar(max), + @parameters varchar(max) +as begin + + declare @exception varchar(max) = error_message() + declare @status varchar(16) = error_state() + declare @code integer = error_number() + declare @exception_id integer + declare @procedure_id integer + declare @message_id integer + + set nocount on + + execute dbo.message_get @exception, @exception_id output + execute dbo.procedure_get @database, @procedure, @procedure_id output + execute dbo.message_get @message, @message_id output + + insert into dbo.Exceptions([procedure], [session], [message], exception, [parameters], [status], code) values + (@procedure_id, @session, @message_id, @exception_id, @parameters, @status, @code) + +end +go + +if object_id(N'dbo.common_tables_fill', N'P') is not null drop procedure dbo.common_tables_fill +go +create procedure dbo.common_tables_fill as begin + + declare @database varchar(64) = 'AnP' + declare @procedure varchar(64) = 'common_tables_fill' + declare @procedure_id integer + declare @local_ip varchar(71) = '::1' + declare @main_user varchar(32) = 'machine' + declare @user varchar(32) + declare @session_id integer = ( + select top 1 id from dbo.[Sessions] where date_out is null and [user] = ( + select top 1 id from dbo.Users where date_out is null and nick = @main_user + ) + ) + declare @has_session bit = case when @session_id is null then 0 else 1 end + declare @ip_id integer = ( + select top 1 id from dbo.Ips where date_out is null and ip = @local_ip + ) + declare @hash_id integer + declare @hash varchar(16) + declare users_cursor cursor local for ( + select @main_user as [name] union all + select 'guest' + ) + declare hashes_cursor cursor local for ( + select 'null' as [name] union all + select 'md5' union all + select 'sha1' union all + select 'sha2_256' union all + select 'sha2_512' + ) + + set nocount on + + if @has_session = 0 begin + if (select top 1 0 from sys.indexes where name = N'ix_sessions_user') is not null + drop index ix_sessions_user on dbo.[Sessions] + if object_id(N'dbo.fk_sessions_user', N'F') is not null + alter table dbo.[Sessions] drop constraint fk_sessions_user + alter table dbo.[Sessions] alter column [user] integer + insert into dbo.[Sessions] default values + set @session_id = scope_identity() + end + + execute dbo.sql_set @database, @procedure, @session_id + execute dbo.procedure_get @database, @procedure, @procedure_id output + + open hashes_cursor + while 1 = 1 begin + fetch next from hashes_cursor into @hash + if @@fetch_status != 0 + break + if (select top 1 0 from dbo.Hashes where date_out is null and [name] = @hash) is null + insert into dbo.Hashes([name]) values(@hash) + end + close hashes_cursor + deallocate hashes_cursor + + execute dbo.hash_get 'null', @hash_id output + + open users_cursor + while 1 = 1 begin + fetch next from users_cursor into @user + if @@fetch_status != 0 + break + if (select top 1 0 from dbo.Users where date_out is null and nick = @user) is null + insert into dbo.Users([hash], nick) values + (@hash_id, @user) + end + close users_cursor + deallocate users_cursor + + if @has_session = 0 begin + update dbo.[Sessions] set [user] = ( + select top 1 id from dbo.Users where date_out is null and nick = @main_user + ) where id = @session_id + alter table dbo.[Sessions] alter column [user] integer not null + alter table dbo.[Sessions] add constraint fk_sessions_user foreign key([user]) references dbo.Users(id) + create nonclustered index ix_sessions_user on dbo.[Sessions]([user] asc) with (fillfactor = 90) + end + + if @ip_id is null begin + insert into dbo.Ips([ip]) values(@local_ip) + set @ip_id = scope_identity() + end + if (select top 1 0 from dbo.SessionsIps where + date_out is null and + [session] = @session_id and + [ip] = @ip_id + ) is null + insert into dbo.SessionsIps([session], [ip]) values(@session_id, @ip_id) + +end +go + +execute dbo.common_tables_fill +go + +if object_id(N'dbo.where_set', N'FN') is not null drop function dbo.where_set +go +create function dbo.where_set( + @old_where varchar(max), + @new_where varchar(max) +) returns varchar(max) as begin + + set @new_where = ltrim(rtrim(isnull(@new_where, ''))) + set @old_where = ltrim(rtrim(isnull(@old_where, ''))) + + return (case + when @new_where = '' then @old_where + when @old_where = '' then @new_where + else '(' + @old_where + ') and (' + @new_where + ')' end) +end +go + +if object_id(N'dbo.tables_list', N'P') is not null drop procedure dbo.tables_list +go +create procedure dbo.tables_list + @view varchar(max), + @page integer, + @items_per_page integer, + @where varchar(max), + @order_by varchar(max), + @has_errors bit, + @pages integer output, + @items integer output, + @results integer output +as begin + + declare @where_string varchar(max) = (case + when @has_errors = 1 then ' where ' + dbo.where_set('0 = 1', @where) + when ltrim(rtrim(isnull(@where, ''))) = '' then '' + else ' where ' + @where end) + declare @query varchar(max) = N'select top 1 @subitems = count(1) from ' + @view + @where_string + + set nocount on + + execute sp_executesql @query, N'@subitems integer output', @subitems = @items output + + set @pages = ceiling(@items / cast(@items_per_page as float)) + set @query = ( + N'select * from ' + @view + @where_string + + (case when ltrim(rtrim(isnull(@order_by, ''))) = '' then '' else N' order by ' + @order_by end) + + N' offset ' + cast((((case + when @page < 1 then 1 + when @page > @pages then @pages + else @page end) - 1) * @items_per_page) as varchar) + N' rows fetch next ' + cast(@items_per_page as varchar) + N' rows only' + ) + + execute sp_executesql @query + set @results = @@rowcount + +end +go \ No newline at end of file diff --git a/SQLServer/AnP.1.11.groups.server.sql b/SQLServer/AnP.1.11.groups.server.sql new file mode 100644 index 0000000..7b91ac7 --- /dev/null +++ b/SQLServer/AnP.1.11.groups.server.sql @@ -0,0 +1,345 @@ +use AnP + +if object_id(N'dbo.groups_tables_delete', N'P') is not null drop procedure dbo.groups_tables_delete +go +create procedure dbo.groups_tables_delete as begin + + set nocount on; + + -- Level 3. + if object_id(N'dbo.GroupsIps', N'U') is not null drop table dbo.GroupsIps + if object_id(N'dbo.GroupsUsers', N'U') is not null drop table dbo.GroupsUsers + + -- Level 1. + if object_id(N'dbo.Groups', N'U') is not null drop table dbo.Groups + + -- Level 0. + if object_id(N'dbo.GroupsTypes', N'U') is not null drop table dbo.GroupsTypes + + -- Level historical. + if object_id(N'dbo.GroupsIpsData', N'U') is not null drop table dbo.GroupsIpsData + if object_id(N'dbo.GroupsUsersData', N'U') is not null drop table dbo.GroupsUsersData + if object_id(N'dbo.GroupsData', N'U') is not null drop table dbo.GroupsData + if object_id(N'dbo.GroupsTypesData', N'U') is not null drop table dbo.GroupsTypesData + +end +go + +if object_id(N'dbo.groups_tables_create', N'P') is not null drop procedure dbo.groups_tables_create +go +create procedure dbo.groups_tables_create as begin + + set nocount on; + + -- Level historical. + if object_id(N'dbo.GroupsTypesData', N'U') is null create table dbo.GroupsTypesData( + id integer not null identity(1,1), + entity integer not null, + [name] varchar(32) not null, + [description] varchar(256), + date_in datetime not null, + date_out datetime, + constraint pk_groups_types primary key clustered(id) + ) + + if object_id(N'dbo.GroupsData', N'U') is null create table dbo.GroupsData( + id integer not null identity(1,1), + entity integer not null, + [procedure] integer not null, + [session] integer not null, + [type] integer not null, + [name] varchar(32) not null, + [description] varchar(256), + date_in datetime not null, + date_out datetime, + constraint pk_groups_data primary key clustered (id) + ) + + if object_id(N'dbo.GroupsIpsData', N'U') is null create table dbo.GroupsIpsData( + id integer not null identity(1,1), + entity integer not null, + [procedure] integer not null, + [session] integer not null, + [group] integer not null, + [ip] integer not null, + belongs bit not null, + date_in datetime not null, + constraint pk_groups_ips_data primary key clustered (id) + ) + + if object_id(N'dbo.GroupsUsersData', N'U') is null create table dbo.GroupsUsersData( + id integer not null identity(1,1), + entity integer not null, + [procedure] integer not null, + [session] integer not null, + [group] integer not null, + [user] integer not null, + belongs bit not null, + date_in datetime not null, + constraint pk_groups_users_data primary key clustered (id) + ) + + -- Level 0. + if object_id(N'dbo.GroupsTypes', N'U') is null create table dbo.GroupsTypes( + id integer not null identity(1,1), + [name] varchar(32) not null, + [description] varchar(256), + date_in datetime not null constraint df_groups_types_date_in default getdate(), + date_out datetime, + constraint pk_groups_types primary key clustered (id), + constraint uk_groups_types_name unique nonclustered ([name] asc) with (fillfactor = 90), + constraint ck_groups_types_name check ([name] != '') + ) + + -- Level 1. + if object_id(N'dbo.Groups', N'U') is null begin + create table dbo.Groups( + id integer not null identity(1,1), + [type] integer not null, + [name] varchar(32) not null, + [description] varchar(256), + date_in datetime not null constraint df_groups_date_in default getdate(), + date_out datetime, + constraint pk_groups primary key clustered (id), + constraint fk_groups_type foreign key ([type]) references dbo.GroupsTypes(id), + constraint uk_groups_type_name unique([name], [type]), + constraint ck_groups_type check ([type] > 0), + constraint ck_groups_name check ([name] != '') + ) + create nonclustered index ix_groups_type on dbo.Groups([type] asc) include ([name]) with (fillfactor = 90) + create nonclustered index ix_groups_name on dbo.Groups([name] asc) include ([type]) with (fillfactor = 90) + end + + -- Level 2. + if object_id(N'dbo.GroupsIps', N'U') is null begin + create table dbo.GroupsIps( + id integer not null identity(1,1), + [group] integer not null, + [ip] integer not null, + belongs bit not null constraint df_groups_ips_belongs default 1, + date_in datetime not null constraint df_groups_ips_date_in default getdate(), + constraint pk_groups_ips primary key clustered (id), + constraint fk_groups_ips_group foreign key ([group]) references dbo.Groups(id), + constraint fk_groups_ips_ip foreign key ([ip]) references dbo.Ips(id), + constraint uk_groups_ips_group_ip unique([group], [ip]), + constraint ck_groups_ips_group check ([group] > 0), + constraint ck_groups_ips_ip check ([ip] > 0) + ) + create nonclustered index ix_groups_ips_group on dbo.GroupsIps([group] asc) include ([ip], belongs) with (fillfactor = 90) + create nonclustered index ix_groups_ips_ip on dbo.GroupsIps([ip] asc) include ([group], belongs) with (fillfactor = 90) + end + + if object_id(N'dbo.GroupsUsers', N'U') is null begin + create table dbo.GroupsUsers( + id integer not null identity(1,1), + [group] integer not null, + [user] integer not null, + belongs bit not null constraint df_groups_users_belongs default 1, + date_in datetime not null constraint df_groups_users_date_in default getdate(), + constraint pk_groups_users primary key clustered (id), + constraint fk_groups_users_group foreign key ([group]) references dbo.Groups(id), + constraint fk_groups_users_user foreign key ([user]) references dbo.Users(id), + constraint uk_groups_users_group_user unique([group], [user]), + constraint ck_groups_users_group check ([group] > 0), + constraint ck_groups_users_user check ([user] > 0) + ) + create nonclustered index ix_groups_users_group on dbo.GroupsUsers([group] asc) include ([user], belongs) with (fillfactor = 90) + create nonclustered index ix_groups_users_user on dbo.GroupsUsers([user] asc) include ([group], belongs) with (fillfactor = 90) + end + +end +go + +if object_id(N'dbo.groups_tables_update', N'P') is not null drop procedure dbo.groups_tables_update +go +create procedure dbo.groups_tables_update as begin + + set nocount on; + +end +go + +execute dbo.groups_tables_delete +go +execute dbo.groups_tables_create +go +execute dbo.groups_tables_update +go + +if object_id(N'dbo.groups_trigger_update', N'TR') is not null drop trigger dbo.groups_trigger_update +go +create trigger dbo.groups_trigger_update on dbo.Groups after insert, update as begin + + declare @database varchar(64) + declare @procedure varchar(64) + declare @procedure_id integer + declare @session integer + declare @id integer + declare @name varchar(32) + declare @description varchar(256) + declare @date_in datetime + declare @date_out datetime + declare groups_cursor cursor local for ( + select id, [name], [description], date_in, date_out from inserted + ) + + set nocount on + + execute dbo.sql_get @database output, @procedure output, @session output + execute dbo.procedure_get @database, @procedure, @procedure_id output + + open groups_cursor + while 1 = 1 begin + fetch next from groups_cursor into @id, @procedure, @session, @name, @description, @date_in, @date_out + if @@fetch_status != 0 + break + insert into dbo.GroupsData(entity, [procedure], [session], [name], [description], date_in, date_out) values + (@id, @procedure, @session, @name, @description, @date_in, @date_out) + update dbo.Groups set data_id = scope_identity() where id = @id + end + close groups_cursor + deallocate groups_cursor + +end +go + +if object_id(N'dbo.groups_ips_trigger_update', N'TR') is not null drop trigger dbo.groups_ips_trigger_update +go +create trigger dbo.groups_ips_trigger_update on dbo.GroupsIps after insert, update as begin + + declare @database varchar(64) + declare @procedure varchar(64) + declare @procedure_id integer + declare @session integer + declare @id integer + declare @group integer + declare @ip integer + declare @belongs bit + declare @date_in datetime + declare groups_ips_cursor cursor local for ( + select id, [group], [ip], belongs, date_in from inserted + ) + + set nocount on + + execute dbo.sql_get @database output, @procedure output, @session output + execute dbo.procedure_get @database, @procedure, @procedure_id output + + open groups_ips_cursor + while 1 = 1 begin + fetch next from groups_ips_cursor into @id, @group, @ip, @belongs, @date_in + if @@fetch_status != 0 + break + insert into dbo.GroupsIpsData(entity, [procedure], [session], [group], [ip], belongs, date_in) values + (@id, @procedure_id, @session_id, @group, @ip, @belongs, @date_in) + end + close groups_ips_cursor + deallocate groups_ips_cursor + +end +go + +if object_id(N'dbo.groups_users_trigger_update', N'TR') is not null drop trigger dbo.groups_users_trigger_update +go +create trigger dbo.groups_users_trigger_update on dbo.GroupsUsers after insert, update as begin + + declare @database varchar(64) + declare @procedure varchar(64) + declare @procedure_id integer + declare @session integer + declare @id integer + declare @group integer + declare @user integer + declare @belongs bit + declare @date_in datetime + declare groups_users_cursor cursor local for ( + select id, [group], [user], belongs, date_in from inserted + ) + + set nocount on + + execute dbo.sql_get @database output, @procedure output, @session output + execute dbo.procedure_get @database, @procedure, @procedure_id output + + open groups_users_cursor + while 1 = 1 begin + fetch next from groups_users_cursor into @id, @procedure, @session, @group, @user, @belongs, @date_in + if @@fetch_status != 0 + break + insert into dbo.GroupsUsersData(entity, [procedure], [session], [group], [user], belongs, date_in) values + (@id, @procedure, @session, @group, @user, @belongs, @date_in) + update dbo.GroupsUsers set data_id = scope_identity() where id = @id + end + close groups_users_cursor + deallocate groups_users_cursor + +end +go + +execute dbo.groups_tables_fill +go + +if object_id(N'dbo.IpsView', N'V') is not null drop view dbo.IpsView +go +create view dbo.IpsView as select + ips.id as id, + ips.[ip] as [ip], + ips.date_in as date_in +from dbo.Ips ips +where ips.date_out is null +go + +if object_id(N'dbo.UsersView', N'V') is not null drop view dbo.UsersView +go +create view dbo.UsersView as select + users.id as id, + users.nick as nick, + users.date_in as date_in +from dbo.Users users +where users.date_out is null +go + +if object_id(N'dbo.GroupsView', N'V') is not null drop view dbo.GroupsView +go +create view dbo.GroupsView as select + groups.id as id, + groups.name as [name], + groups.description as [description], + groups.date_in as date_in +from dbo.Groups groups +where groups.date_out is null +go + +if object_id(N'dbo.GroupsIpsView', N'V') is not null drop view dbo.GroupsIpsView +go +create view dbo.GroupsIpsView as select + groups_ips.id as id, + groups.id as group_id, + groups.[name] as [group], + ips.id as ip_id, + ips.[ip] as [ip], + isnull(groups_ips.belongs, 0) as belongs, + groups_ips.date_in as date_in +from dbo.GroupsViews groups +join dbo.IpsViews ips on 1 = 1 +left join dbo.GroupsIps groups_ips on + groups_ips.[group] = groups.id + groups_ips.[ip] = ips.id +go + +if object_id(N'dbo.GroupsUsersView', N'V') is not null drop view dbo.GroupsUsersView +go +create view dbo.GroupsUsersView as select + groups_users.id as id, + groups.id as group_id, + groups.[name] as [group], + users.id as user_id, + users.nick as [user], + isnull(groups_users.belongs, 0) as belongs, + groups_users.date_in as date_in +from dbo.GroupsViews groups +join dbo.UsersViews users on 1 = 1 +left join dbo.GroupsUsers groups_users on + groups_users.[group] = groups.id + groups_users.[user] = users.id +go \ No newline at end of file diff --git a/SQLServer/Dockerfile b/SQLServer/Dockerfile new file mode 100644 index 0000000..a2b8b3f --- /dev/null +++ b/SQLServer/Dockerfile @@ -0,0 +1,4 @@ +from mcr.microsoft.com/mssql/server:2022-latest +expose 1433/tcp +user root +env ACCEPT_EULA=Y \ No newline at end of file diff --git a/SQLServer/docker.rebuild.sh b/SQLServer/docker.rebuild.sh new file mode 100755 index 0000000..3710a78 --- /dev/null +++ b/SQLServer/docker.rebuild.sh @@ -0,0 +1,4 @@ +#!/bin/bash +directory=`dirname $(readlink -f "$0")` +[ "$(docker images -q anp:sql-server 2>/dev/null)" ] && docker image remove anp:sql-server --force +docker build -f $directory/Dockerfile -t anp:sql-server $directory --no-cache \ No newline at end of file diff --git a/Tools/reinstall.dockers.sh b/Tools/reinstall.dockers.sh new file mode 100755 index 0000000..8e992e2 --- /dev/null +++ b/Tools/reinstall.dockers.sh @@ -0,0 +1,6 @@ +#!/bin/bash +directory=`dirname $(readlink -f "$0")` +for file in $(find $directory/.. -type f -name "docker.rebuild.sh"); do + chmod +x $file + $file +done \ No newline at end of file diff --git a/Tools/run.cs.server.sh b/Tools/run.cs.server.sh new file mode 100755 index 0000000..43a2f30 --- /dev/null +++ b/Tools/run.cs.server.sh @@ -0,0 +1,4 @@ +#!/bin/bash +cd `dirname $(readlink -f "$0")`/.. +docker exec -it anp-dotnet dotnet run --project /workspace/CSharp/AnP.csproj -f net10.0 +cd Tools \ No newline at end of file diff --git a/Tools/run.server.sh b/Tools/run.server.sh new file mode 100755 index 0000000..2cb9db4 --- /dev/null +++ b/Tools/run.server.sh @@ -0,0 +1,3 @@ +#!/bin/bash +directory=`dirname $(readlink -f "$0")` +python3 "$directory/../Python/server.py" diff --git a/Tools/sass.sh b/Tools/sass.sh new file mode 100755 index 0000000..bac3be8 --- /dev/null +++ b/Tools/sass.sh @@ -0,0 +1,3 @@ +#!/bin/bash +directory=`dirname $(readlink -f "$0")` +sass $directory/../Public/scss/AnP.scss ../Public/scss/AnP.css; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6e64822 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +services: + + dotnet: + image: anp:dotnet + container_name: anp-dotnet + volumes: + - ./DotNET/scripts:/scripts + - ./CSharp:/workspace/CSharp + - ./VB:/workspace/VB + - ./FSharp:/workspace/FSharp + - ./CPP:/workspace/CPP + - ./JSON:/workspace/JSON + - ./Public:/workspace/Public + ports: + - 28080:80/tcp + entrypoint: /scripts/entrypoint.sh + networks: + anp-network: + ipv4_address: 172.20.11.223 + + anp-sql-server: + image: anp:sql-server + container_name: anp-sql-server + environment: + - MSSQL_SA_PASSWORD=Abc123.. + volumes: + - ./SQLServer/data:/var/opt/mssql + - ./SQLServer/temporary:/temporary + - ./SQLServer/scripts:/scripts + ports: + - 21433:1433/tcp + networks: + anp-network: + ipv4_address: 172.20.11.222 + +networks: + anp-network: + driver: bridge + ipam: + config: + - subnet: 172.20.11.0/24 \ No newline at end of file diff --git a/version b/version new file mode 100644 index 0000000..8a9ecc2 --- /dev/null +++ b/version @@ -0,0 +1 @@ +0.0.1 \ No newline at end of file