699 lines
44 KiB
C#
699 lines
44 KiB
C#
using System.Security.Cryptography.Pkcs;
|
|
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace perubahan;
|
|
|
|
public static partial class APIHandler
|
|
{
|
|
public static void Handle(IApplicationBuilder App)
|
|
{
|
|
App
|
|
//===========TEST ONLY============
|
|
.Map("/hostcheck", host =>
|
|
{
|
|
host.Run(async runner =>
|
|
{
|
|
await runner.WriteJsonResponse(200, $"{runner.Request.Host} : {HttpOnly.Domain}");
|
|
});
|
|
})
|
|
.Map("/numbertest", test =>
|
|
{
|
|
test.Run(async runner =>
|
|
{
|
|
Dictionary<string, JsonElement> ELE = (await runner.TryGetBodyJsonAsync(["num"], CTS.Token))!;
|
|
await runner.WriteJsonResponse(200, $"{ELE["num"].GetByte()}");
|
|
});
|
|
})
|
|
//============MISC=================
|
|
.Map("/updatecache", cache =>
|
|
{
|
|
cache.Run(async runner =>
|
|
{
|
|
await UpdateCache();
|
|
await runner.WriteJsonResponse(StatusCodes.Status200OK, "Cache Updated.");
|
|
});
|
|
})
|
|
// -------ADD-/sse-later--------
|
|
//===========UNITS=================
|
|
.Map("/getunits", units =>
|
|
{
|
|
units.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3)) return;
|
|
await runner.WriteJsonResponse(StatusCodes.Status200OK, "Success", Deployments);
|
|
});
|
|
})
|
|
//=======AGENTS & USERS============
|
|
.Map("/getagents", agents =>
|
|
{
|
|
agents.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3)) return;
|
|
await runner.WriteJsonResponse(StatusCodes.Status200OK, "Success", Agents);
|
|
});
|
|
})
|
|
.Map("/addagent", agent =>
|
|
{
|
|
agent.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(1, "POST", true)) return;
|
|
if (await runner.TryGetBodyJsonAsync(["agentid", "name", "jabatan", "deplid", "skangkat", "tmt", "skper", "tgper", "vision", "mission", "photo", "seleksi", "nilaipilih", "eviden", "dokumentasi", "createuser", "uname", "pass", "level"], CTS.Token) is Dictionary<string, JsonElement> InElement)
|
|
{
|
|
string AgentID = InElement["agentid"].GetString() ?? string.Empty;
|
|
string Name = InElement["name"].GetString() ?? string.Empty;
|
|
string Jabatan = InElement["jabatan"].GetString() ?? string.Empty;
|
|
short DeploymentID = InElement["deplid"].GetInt16();
|
|
string SKAngkat = InElement["skangkat"].GetString() ?? string.Empty;
|
|
DateOnly TMT = DateOnly.Parse(InElement["tmt"].GetString()?[..10] ?? "1970-01-01");
|
|
string SKPer = InElement["skper"].GetString() ?? string.Empty;
|
|
DateOnly? TGPer = InElement["tgper"].GetString()?.Length > 0 ? DateOnly.Parse(InElement["tgper"].GetString()![..10]) : null;
|
|
string Vision = InElement["vision"].GetString() ?? "-";
|
|
string Mission = InElement["mission"].GetString() ?? "-";
|
|
string Photo = InElement["photo"].GetString() ?? string.Empty;
|
|
string PhotoURL = string.Empty;
|
|
string? Seleksi = InElement["seleksi"].GetString();
|
|
DateOnly? NilaiPilih = InElement["nilaipilih"].ValueKind == JsonValueKind.Null ? null : DateOnly.FromDateTime(InElement["nilaipilih"].GetDateTime());
|
|
string? Eviden = InElement["eviden"].GetString();
|
|
string? Dokumentasi = InElement["dokumentasi"].GetString();
|
|
bool CreateUser = InElement["createuser"].GetBoolean();
|
|
string UName = InElement["uname"].GetString() ?? string.Empty;
|
|
string PlainPass = InElement["pass"].GetString() ?? string.Empty;
|
|
byte Level = InElement["level"].GetByte();
|
|
Match PhotoMatch = Base64Regex().Match(Photo);
|
|
if (AgentID.Equals(string.Empty) ||
|
|
Name.Equals(string.Empty) ||
|
|
Jabatan.Equals(string.Empty) ||
|
|
//DeploymentID.Equals(0) ||
|
|
SKAngkat.Equals(string.Empty) ||
|
|
TMT.Equals(DateOnly.Parse("1970-01-01")) ||
|
|
(!SKPer.Equals(string.Empty) && TGPer is null) ||
|
|
(CreateUser && UName.Equals(string.Empty)) ||
|
|
(CreateUser && PlainPass.Equals(string.Empty)) ||
|
|
(!Photo.Equals(string.Empty) && !PhotoMatch.Success) ||
|
|
(!await runner.RequestValidated(Level, "POST"))
|
|
)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "One or more input(s) are not acceptable, in an unsupported format, or an attempt to create user account of a higher level than the creator is made.");
|
|
return;
|
|
}
|
|
if (!Photo.Equals(string.Empty))
|
|
{
|
|
string Format = PhotoMatch.Groups["format"].Value.ToLowerInvariant();
|
|
string Data = PhotoMatch.Groups["data"].Value;
|
|
byte[] ImageBytes = Convert.FromBase64String(Data);
|
|
uint CRC32Hash = Crc32.Compute(ImageBytes);
|
|
string PhotoFileName = $"{CRC32Hash:X8}.{(Format == "jpeg" ? "jpg" : Format)}";
|
|
string PhotoPath = Path.Combine(AppContext.BaseDirectory, "wwwroot/assets/images/uploads", PhotoFileName);
|
|
if (!File.Exists(PhotoPath)) await File.WriteAllBytesAsync(PhotoPath, ImageBytes, CTS.Token);
|
|
PhotoURL = Path.Combine("/assets/images/uploads", PhotoFileName);
|
|
}
|
|
Agent NewAgent = new(AgentID, Name, Jabatan, DeploymentID, SKAngkat, TMT, SKPer.Length == 0 ? null : SKPer, SKPer.Length == 0 ? null : TGPer, Vision, Mission, Photo.Length == 0 ? null : PhotoURL, Seleksi, NilaiPilih, Eviden, Dokumentasi);
|
|
await RunTransactionAsync(CS, async (Conn, Trans) =>
|
|
{
|
|
using (SqlCommand CreateAgent = Conn.CreateCommand())
|
|
{
|
|
CreateAgent.Transaction = Trans;
|
|
CreateAgent.CommandText = "INSERT INTO agents VALUES(@agid, @nama, @jabt, @deid, @skng, @tmt, @skpr, @tmpr, @visi, @misi, @poto, @sl, @np, @ev, @do)";
|
|
CreateAgent.Parameters.AddWithValue("@agid", AgentID);
|
|
CreateAgent.Parameters.AddWithValue("@nama", Name);
|
|
CreateAgent.Parameters.AddWithValue("@jabt", Jabatan);
|
|
CreateAgent.Parameters.AddWithValue("@deid", DeploymentID);
|
|
CreateAgent.Parameters.AddWithValue("@skng", SKAngkat);
|
|
CreateAgent.Parameters.AddWithValue("@tmt", TMT);
|
|
CreateAgent.Parameters.AddWithValue("@skpr", SKPer.Equals(string.Empty) ? DBNull.Value : SKPer);
|
|
CreateAgent.Parameters.AddWithValue("@tmpr", SKPer.Equals(string.Empty) ? DBNull.Value : TGPer);
|
|
CreateAgent.Parameters.AddWithValue("@visi", Vision);
|
|
CreateAgent.Parameters.AddWithValue("@misi", Mission);
|
|
CreateAgent.Parameters.AddWithValue("@poto", PhotoURL.Equals(string.Empty) ? DBNull.Value : PhotoURL);
|
|
CreateAgent.Parameters.AddWithValue("@sl", string.IsNullOrEmpty(Seleksi) ? DBNull.Value : Seleksi);
|
|
CreateAgent.Parameters.AddWithValue("@np", NilaiPilih is null ? DBNull.Value : NilaiPilih);
|
|
CreateAgent.Parameters.AddWithValue("@ev", string.IsNullOrEmpty(Eviden) ? DBNull.Value : Eviden);
|
|
CreateAgent.Parameters.AddWithValue("@do", string.IsNullOrEmpty(Dokumentasi) ? DBNull.Value : Dokumentasi);
|
|
_ = await CreateAgent.ExecuteNonQueryAsync();
|
|
Agents.Add(NewAgent);
|
|
}
|
|
if (CreateUser)
|
|
{
|
|
string HashedPass = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(PlainPass))).ToLowerInvariant();
|
|
User NewUser = new(UName, AgentID, HashedPass, Level, true);
|
|
using (SqlCommand CreateUser = Conn.CreateCommand())
|
|
{
|
|
CreateUser.Transaction = Trans;
|
|
CreateUser.CommandText = "INSERT INTO useraccounts VALUES(@unam, @pass, @agid, @levl, 1)";
|
|
CreateUser.Parameters.AddWithValue("@unam", UName);
|
|
CreateUser.Parameters.AddWithValue("@pass", HashedPass);
|
|
CreateUser.Parameters.AddWithValue("@agid", AgentID);
|
|
CreateUser.Parameters.AddWithValue("@levl", Level);
|
|
_ = await CreateUser.ExecuteNonQueryAsync();
|
|
}
|
|
UserAccounts.TryAdd(UName, NewUser);
|
|
}
|
|
}, CTS.Token
|
|
);
|
|
string OutMessage = CreateUser ? "New Agent and respective User Account created" : "New Agent created. User account creation is possible.";
|
|
await runner.WriteJsonResponse(StatusCodes.Status201Created, OutMessage, CreateUser ? new SafeUser(UName, AgentID, Level, true) : NewAgent);
|
|
}
|
|
});
|
|
})
|
|
.Map("/chagent", agent =>
|
|
{
|
|
agent.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(0, "POST", true)) return;
|
|
if (await runner.TryGetBodyJsonAsync(["agentid", "photo", "updates"], CTS.Token) is Dictionary<string, JsonElement> InElement)
|
|
{
|
|
if (InElement["updates"].ValueKind != JsonValueKind.Object) return;
|
|
string AgentID = InElement["agentid"].GetString() ?? string.Empty;
|
|
string Photo = InElement["photo"].GetString() ?? string.Empty;
|
|
string PhotoURL = "";
|
|
Match PhotoMatch = Base64Regex().Match(Photo);
|
|
if (!Photo.Equals(string.Empty))
|
|
{
|
|
string Format = PhotoMatch.Groups["format"].Value.ToLowerInvariant();
|
|
string Data = PhotoMatch.Groups["data"].Value;
|
|
byte[] ImageBytes = Convert.FromBase64String(Data);
|
|
uint CRC32Hash = Crc32.Compute(ImageBytes);
|
|
string PhotoFileName = $"{CRC32Hash:X8}.{(Format == "jpeg" ? "jpg" : Format)}";
|
|
string PhotoPath = Path.Combine(AppContext.BaseDirectory, "wwwroot/assets/images/uploads", PhotoFileName);
|
|
if (!File.Exists(PhotoPath)) await File.WriteAllBytesAsync(PhotoPath, ImageBytes, CTS.Token);
|
|
PhotoURL = Path.Combine("/assets/images/uploads", PhotoFileName);
|
|
}
|
|
JsonElement UpdateFields = InElement["updates"];
|
|
using SqlDataReader Updated = await RunReaderAsync(CS, "", Comm =>
|
|
{
|
|
StringBuilder CommandBuilder = new();
|
|
CommandBuilder.Append("UPDATE agents SET");
|
|
foreach (JsonProperty Prop in UpdateFields.EnumerateObject())
|
|
{
|
|
Comm.Parameters.AddWithValue($"@p{Prop.Name}", Prop.Value.ValueKind == JsonValueKind.Null ? DBNull.Value : Prop.Value.ValueKind == JsonValueKind.String ? Prop.Value.GetString() : Prop.Value.GetInt16());
|
|
CommandBuilder.Append($" [{Prop.Name}] = @p{Prop.Name},");
|
|
}
|
|
Comm.Parameters.AddWithValue("@pagentid", AgentID);
|
|
if (!string.IsNullOrEmpty(PhotoURL))
|
|
{
|
|
CommandBuilder.Append($" [photourl] = @purl,");
|
|
Comm.Parameters.AddWithValue("@purl", PhotoURL);
|
|
}
|
|
CommandBuilder.Remove(CommandBuilder.Length - 1, 1);
|
|
CommandBuilder.Append(" OUTPUT INSERTED.* WHERE agentid = @pagentid");
|
|
Comm.CommandText = CommandBuilder.ToString();
|
|
}, CTS.Token);
|
|
Agent UpAgent = (await Updated.ToListAsync<Agent>(a => new
|
|
(
|
|
AgentID,
|
|
(string)a["name"],
|
|
(string)a["jabatan"],
|
|
(short)a["deplid"],
|
|
(string)a["skangkat"],
|
|
DateOnly.FromDateTime((DateTime)a["tmt"]),
|
|
a["skperubahan"] == DBNull.Value ? null : (string)a["skperubahan"],
|
|
a["tgperubahan"] == DBNull.Value ? null : DateOnly.FromDateTime((DateTime)a["tgperubahan"]),
|
|
a["vision"] == DBNull.Value ? null : (string)a["vision"],
|
|
a["mission"] == DBNull.Value ? null : (string)a["mission"],
|
|
a["photourl"] == DBNull.Value ? null : (string)a["photourl"],
|
|
a["seleksi"] == DBNull.Value ? null : (string)a["seleksi"],
|
|
a["nilaipilih"] == DBNull.Value ? null : DateOnly.FromDateTime((DateTime)a["nilaipilih"]),
|
|
a["eviden"] == DBNull.Value ? null : (string)a["eviden"],
|
|
a["dokumentasi"] == DBNull.Value ? null : (string)a["dokumentasi"]
|
|
), CTS.Token))[0];
|
|
int AgentIdx = Agents.FindIndex(a => a.AgentID == UpAgent.AgentID);
|
|
Agents[AgentIdx] = Agents[AgentIdx] with
|
|
{
|
|
Name = UpAgent.Name,
|
|
Jabatan = UpAgent.Jabatan,
|
|
DeplID = UpAgent.DeplID,
|
|
SKAngkat = UpAgent.SKAngkat,
|
|
TMT = UpAgent.TMT,
|
|
SKPerb = UpAgent.SKPerb,
|
|
TMUbah = UpAgent.TMUbah,
|
|
Vision = UpAgent.Vision,
|
|
Mission = UpAgent.Mission,
|
|
PhotoURL = UpAgent.PhotoURL,
|
|
Seleksi = UpAgent.Seleksi,
|
|
NilaiPilih = UpAgent.NilaiPilih,
|
|
Eviden = UpAgent.Eviden,
|
|
Dokumentasi = UpAgent.Dokumentasi
|
|
};
|
|
await runner.WriteJsonResponse(StatusCodes.Status202Accepted, "Data updated.", UpAgent);
|
|
}
|
|
});
|
|
})
|
|
.Map("/getusers", users =>
|
|
{
|
|
users.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3)) return;
|
|
List<SafeUser> SafeAccounts = [];
|
|
foreach (User UnsafeUser in UserAccounts.Values.ToList())
|
|
{
|
|
SafeAccounts.Add(SafeUser.FromUser(UnsafeUser));
|
|
}
|
|
await runner.WriteJsonResponse(StatusCodes.Status200OK, "Success", SafeAccounts);
|
|
});
|
|
})
|
|
.Map("/passwd", passwd =>
|
|
{
|
|
passwd.Run(async runner =>
|
|
{
|
|
if
|
|
(
|
|
!await runner.RequestValidated(0, "POST", true) //has to pass this before trying to get bodyjsonasync.
|
|
//Let it be for now, move the json check login into trygetjson for later projects.
|
|
|| await runner.TryGetBodyJsonAsync(["username", "password"], CTS.Token) is not Dictionary<string, JsonElement> InElement
|
|
// || !(await runner.RequestValidated(InElement["username"].GetString() ?? string.Empty, "POST") || await runner.RequestValidated(0, "POST"))
|
|
) return;
|
|
if (InElement["password"].GetString() is not string PlainPass || PlainPass.Equals(string.Empty) || InElement["username"].GetString() is not string Username || Username.Equals(string.Empty))
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "Username and/or Password can't be empty");
|
|
return;
|
|
}
|
|
string HashedPass = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(PlainPass))).ToLowerInvariant();
|
|
_ = await RunNonQueryAsync(CS, "UPDATE useraccounts SET [pass] = @hp WHERE [uname] = @un", Act =>
|
|
{
|
|
Act.Parameters.AddWithValue("@un", Username);
|
|
Act.Parameters.AddWithValue("@hp", HashedPass);
|
|
}, CTS.Token);
|
|
UserAccounts[Username] = UserAccounts[Username] with { Password = HashedPass };
|
|
await runner.WriteJsonResponse(StatusCodes.Status202Accepted, "Password Updated.");
|
|
});
|
|
})
|
|
.Map("/adduser", user =>
|
|
{
|
|
user.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(ValidMethod: "POST", CheckJson: true) || await runner.TryGetBodyJsonAsync(["username", "password", "agentid", "level"], CTS.Token) is not Dictionary<string, JsonElement> InElement) return;
|
|
if (
|
|
InElement["username"].GetString() is not string Username ||
|
|
InElement["password"].GetString() is not string PlainPass ||
|
|
InElement["agentid"].GetString() is not string AgentID ||
|
|
InElement["level"].GetByte() is byte Level && Level == 0
|
|
)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "String fields should not be empty and level should not be zero or less.");
|
|
return;
|
|
}
|
|
string HashedPass = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(PlainPass))).ToLowerInvariant();
|
|
await RunNonQueryAsync(CS, "INSERT INTO useraccounts VALUES(@un, @pw, @ai, @lv, 1)", Conf =>
|
|
{
|
|
Conf.Parameters.AddWithValue("@un", Username);
|
|
Conf.Parameters.AddWithValue("@pw", HashedPass);
|
|
Conf.Parameters.AddWithValue("@ai", AgentID);
|
|
Conf.Parameters.AddWithValue("@lv", Level);
|
|
}, CTS.Token);
|
|
UserAccounts.TryAdd(Username, new(Username, AgentID, HashedPass, Level, true));
|
|
await runner.WriteJsonResponse(StatusCodes.Status201Created, $"New user account created for Agent ID {AgentID}. Check data for created username", Username);
|
|
});
|
|
})
|
|
.Map("/toggleuser", userstate =>
|
|
{
|
|
userstate.Run(async runner =>
|
|
{
|
|
if
|
|
(
|
|
!await runner.RequestValidated(0, "POST", true) //has to pass this before trying to get bodyjsonasync.
|
|
//Let it be for now, move the json check login into trygetjson for later projects.
|
|
|| await runner.TryGetBodyJsonAsync(["username"], CTS.Token) is not Dictionary<string, JsonElement> InElement
|
|
|| InElement["username"].GetString() is not string Username
|
|
) return;
|
|
if (UserAccounts[Username] is not User FoundUser)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status404NotFound, "Username not found.", Username);
|
|
return;
|
|
}
|
|
await RunNonQueryAsync(CS, "UPDATE useraccounts SET [active] = @ac WHERE [uname]=@un", Conf =>
|
|
{
|
|
Conf.Parameters.AddWithValue("@un", Username);
|
|
Conf.Parameters.AddWithValue("@ac", !FoundUser.Active);
|
|
}, CTS.Token);
|
|
UserAccounts[Username] = UserAccounts[Username] with { Active = !FoundUser.Active };
|
|
await runner.WriteJsonResponse(StatusCodes.Status202Accepted, "User account active state updated. See data for current active state", !FoundUser.Active);
|
|
});
|
|
})
|
|
.Map("/rmagent", agent =>
|
|
{
|
|
agent.Run(async runner =>
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status501NotImplemented, "Not yet finished.");
|
|
});
|
|
})
|
|
.Map("/rmuser", user =>
|
|
{
|
|
user.Run(async runner =>
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status501NotImplemented, "Not yet finished.");
|
|
});
|
|
})
|
|
//=========ACTIVITIES=============
|
|
.Map("/getprokers", prokers =>
|
|
{
|
|
prokers.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3) || !Auth.TryGetUser(runner, out SafeUser CurrUser)) return;
|
|
List<ProkerJournal> Journals = [];
|
|
using SqlDataReader Prokers = await RunReaderAsync(CS, "SELECT * FROM [AllJournal] WHERE [ownerid] = @oi OR @oi IS NULL ORDER BY [ownerid] ASC, [year] DESC, [kegiatan] ASC, [prokerid] ASC, [prokerjid] DESC", Query =>
|
|
{
|
|
Query.Parameters.AddWithValue("@oi", CurrUser.Level < 3 ? DBNull.Value : CurrUser.AgentID);
|
|
}, CTS.Token);
|
|
Journals = await Prokers.ToListAsync<ProkerJournal>(J => new(
|
|
(string)J["prokerid"],
|
|
(string)J["ownerid"],
|
|
(byte)J["kegiatan"],
|
|
(string)J["sasaran"],
|
|
J["startday"] == DBNull.Value ? null : (byte)J["startday"],
|
|
(byte)J["startmonth"],
|
|
(short)J["year"],
|
|
(byte)J["timetarget"],
|
|
(bool)J["timeunit"],
|
|
(string)J["enttarget"],
|
|
(string)J["indicators"],
|
|
(string)J["actions"],
|
|
(string)J["prokerjid"],
|
|
(string)J["submitter"],
|
|
(string)J["notes"],
|
|
(byte)J["status"],
|
|
J["document"] == DBNull.Value ? null : (string)J["document"],
|
|
(DateTime)J["timestamp"]
|
|
));
|
|
await runner.WriteJsonResponse(StatusCodes.Status200OK, "Proker Journal Fetched.", Journals);
|
|
});
|
|
})
|
|
.Map("/mkproker", proker =>
|
|
{
|
|
proker.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3, "POST", true) ||
|
|
await runner.TryGetBodyJsonAsync(["prokerid", "agentID", "kegiatan", "sasaran", "startDay", "startMonth",
|
|
"year", "timeTarget", "targetUnit", "entTarget", "indicator",
|
|
"action"], CTS.Token) is not Dictionary<string, JsonElement> InElement) return;
|
|
if (InElement["prokerid"].GetString() is not string ProkerID || ProkerID.Equals(string.Empty)
|
|
|| InElement["agentID"].GetString() is not string AgentID || AgentID.Equals(string.Empty)
|
|
|| InElement["kegiatan"].GetByte() is byte Kegiatan && (Kegiatan < 0 || Kegiatan > 4)
|
|
|| InElement["sasaran"].GetString() is not string Sasaran || Sasaran.Equals(string.Empty)
|
|
|| InElement["startMonth"].GetByte() is byte StartMonth && (StartMonth < 1 || StartMonth > 12)
|
|
|| InElement["year"].GetInt16() is short Year && Year < 2020
|
|
|| InElement["timeTarget"].GetByte() is byte TimeTarget && TimeTarget < 1
|
|
|| InElement["targetUnit"].GetByte() is byte targetUnit && (targetUnit < 0 || targetUnit > 1)
|
|
|| InElement["entTarget"].GetString() is not string EntTarget || EntTarget.Equals(string.Empty)
|
|
|| InElement["indicator"].GetString() is not string Indicator || Indicator.Equals(string.Empty)
|
|
|| InElement["action"].GetString() is not string Action || Action.Equals(string.Empty))
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "Required property is of invalid format.");
|
|
return;
|
|
}
|
|
byte? StartDay = InElement["startDay"].ValueKind == JsonValueKind.Null ? null : InElement["startDay"].GetByte();
|
|
bool TargetUnit = targetUnit == 1;
|
|
await RunTransactionAsync(CS, async (Conn, Trans) =>
|
|
{
|
|
using (SqlCommand CreateProker = Conn.CreateCommand())
|
|
{
|
|
CreateProker.Transaction = Trans;
|
|
CreateProker.CommandText = "INSERT INTO [proker] VALUES(@pkid, @agid, @kgtn, @ssrn, @stdy, @stmh, @year, @tmtg, @tmun, @entg, @indc, @actn)";
|
|
CreateProker.Parameters.AddWithValue("@pkid", ProkerID);
|
|
CreateProker.Parameters.AddWithValue("@agid", AgentID);
|
|
CreateProker.Parameters.AddWithValue("@kgtn", Kegiatan);
|
|
CreateProker.Parameters.AddWithValue("@ssrn", Sasaran);
|
|
CreateProker.Parameters.AddWithValue("@stdy", StartDay is null ? DBNull.Value : StartDay);
|
|
CreateProker.Parameters.AddWithValue("@stmh", StartMonth);
|
|
CreateProker.Parameters.AddWithValue("@year", Year);
|
|
CreateProker.Parameters.AddWithValue("@tmtg", TimeTarget);
|
|
CreateProker.Parameters.AddWithValue("@tmun", TargetUnit);
|
|
CreateProker.Parameters.AddWithValue("@entg", EntTarget);
|
|
CreateProker.Parameters.AddWithValue("@indc", Indicator);
|
|
CreateProker.Parameters.AddWithValue("@actn", Action);
|
|
_ = await CreateProker.ExecuteNonQueryAsync(CTS.Token);
|
|
}
|
|
using SqlCommand CreateJournal = Conn.CreateCommand();
|
|
DateTime Now = DateTime.Now;
|
|
CreateJournal.Transaction = Trans;
|
|
CreateJournal.CommandText = "INSERT INTO [proker_journal] VALUES(@pjid, @pkid, @agid, @stts, @nots, @docs, @tstp)";
|
|
CreateJournal.Parameters.AddWithValue("@pjid", GenerateUuidV7(Now));
|
|
CreateJournal.Parameters.AddWithValue("@pkid", ProkerID);
|
|
CreateJournal.Parameters.AddWithValue("@agid", AgentID);
|
|
CreateJournal.Parameters.AddWithValue("@stts", 0);
|
|
CreateJournal.Parameters.AddWithValue("@nots", "Draft");
|
|
CreateJournal.Parameters.AddWithValue("@docs", DBNull.Value);
|
|
CreateJournal.Parameters.AddWithValue("@tstp", Now);
|
|
_ = await CreateJournal.ExecuteNonQueryAsync(CTS.Token);
|
|
}, CTS.Token);
|
|
await runner.WriteJsonResponse(StatusCodes.Status201Created, "Proker Created and Journaled.");
|
|
});
|
|
})
|
|
.Map("/updateproker", proker =>
|
|
{
|
|
proker.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3, "POST", true) ||
|
|
await runner.TryGetBodyJsonAsync(["prokerid", "kegiatan", "sasaran", "startDay", "startMonth",
|
|
"year", "timeTarget", "targetUnit", "entTarget", "indicator",
|
|
"action"], CTS.Token) is not Dictionary<string, JsonElement> InElement) return;
|
|
if (InElement["prokerid"].GetString() is not string ProkerID || ProkerID.Equals(string.Empty)
|
|
|| InElement["kegiatan"].GetByte() is byte Kegiatan && (Kegiatan < 0 || Kegiatan > 4)
|
|
|| InElement["sasaran"].GetString() is not string Sasaran || Sasaran.Equals(string.Empty)
|
|
|| InElement["startMonth"].GetByte() is byte StartMonth && (StartMonth < 1 || StartMonth > 12)
|
|
|| InElement["year"].GetInt16() is short Year && Year < 2020
|
|
|| InElement["timeTarget"].GetByte() is byte TimeTarget && TimeTarget < 1
|
|
|| InElement["targetUnit"].GetByte() is byte targetUnit && (targetUnit < 0 || targetUnit > 1)
|
|
|| InElement["entTarget"].GetString() is not string EntTarget || EntTarget.Equals(string.Empty)
|
|
|| InElement["indicator"].GetString() is not string Indicator || Indicator.Equals(string.Empty)
|
|
|| InElement["action"].GetString() is not string Action || Action.Equals(string.Empty))
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "Required property is of invalid format.");
|
|
return;
|
|
}
|
|
byte? StartDay = InElement["startDay"].ValueKind == JsonValueKind.Null ? null : InElement["startDay"].GetByte();
|
|
bool TargetUnit = targetUnit == 1;
|
|
_ = await RunNonQueryAsync(CS, "UPDATE [proker] SET [kegiatan] = @kgtn, [sasaran] = @ssrn, startday = @stdy, startmonth = @stmh, year = @year, timetarget = @tmtg, timeunit = @tmun, enttarget = @entg, indicators = @indc, actions = @actn FROM [proker] JOIN [LatestJournal] ON [proker].[prokerid] = [LatestJournal].[prokerid] WHERE [proker].[prokerid] = @pkid AND [LatestJournal].[status] < 2", UpdateProker =>
|
|
{
|
|
UpdateProker.Parameters.AddWithValue("@pkid", ProkerID);
|
|
UpdateProker.Parameters.AddWithValue("@kgtn", Kegiatan);
|
|
UpdateProker.Parameters.AddWithValue("@ssrn", Sasaran);
|
|
UpdateProker.Parameters.AddWithValue("@stdy", StartDay is null ? DBNull.Value : StartDay);
|
|
UpdateProker.Parameters.AddWithValue("@stmh", StartMonth);
|
|
UpdateProker.Parameters.AddWithValue("@year", Year);
|
|
UpdateProker.Parameters.AddWithValue("@tmtg", TimeTarget);
|
|
UpdateProker.Parameters.AddWithValue("@tmun", TargetUnit);
|
|
UpdateProker.Parameters.AddWithValue("@entg", EntTarget);
|
|
UpdateProker.Parameters.AddWithValue("@indc", Indicator);
|
|
UpdateProker.Parameters.AddWithValue("@actn", Action);
|
|
}, CTS.Token);
|
|
await runner.WriteJsonResponse(StatusCodes.Status202Accepted, "Proker Updated.");
|
|
});
|
|
})
|
|
.Map("/processpk", proker =>
|
|
{
|
|
proker.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(2, "POST", true) || !Auth.TryGetUser(runner, out SafeUser CurrUser) ||
|
|
await runner.TryGetBodyJsonAsync(["agentid", "year", "newstatus", "notes"], CTS.Token) is not Dictionary<string, JsonElement> InElement) return;
|
|
if (CurrUser.Level != 2)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status401Unauthorized, "Only Level 2 Users may process Proker submission.");
|
|
return;
|
|
}
|
|
if (
|
|
InElement["agentid"].GetString() is not string AgentID || AgentID.Equals(string.Empty) ||
|
|
InElement["year"].GetInt16() is short Year && Year < 2020 ||
|
|
InElement["newstatus"].GetByte() is byte Status && Status != 1 && Status != 3 ||
|
|
InElement["notes"].GetString() is not string Notes || Notes.Equals(string.Empty)
|
|
)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "Required property is of invalid format.");
|
|
return;
|
|
}
|
|
await RunTransactionAsync(CS, async (Conn, Trans) =>
|
|
{
|
|
List<string> PKIDList = [];
|
|
using (SqlCommand PKIDFetch = Conn.CreateCommand())
|
|
{
|
|
PKIDFetch.Transaction = Trans;
|
|
PKIDFetch.CommandText = "SELECT [prokerid] FROM [LatestJournal] WHERE [ownerid] = @oi AND [year] = @yr AND [status] = 2 ORDER BY [kegiatan]";
|
|
PKIDFetch.Parameters.AddWithValue("@oi", AgentID);
|
|
PKIDFetch.Parameters.AddWithValue("@yr", Year);
|
|
using SqlDataReader PKRd = await PKIDFetch.ExecuteReaderAsync(CTS.Token);
|
|
PKIDList = await PKRd.ToListAsync(PKID => (string)PKID["prokerid"], CTS.Token);
|
|
}
|
|
foreach (string PKID in PKIDList)
|
|
{
|
|
DateTime Now = DateTime.Now;
|
|
using SqlCommand PJUpdate = Conn.CreateCommand();
|
|
PJUpdate.Transaction = Trans;
|
|
PJUpdate.CommandText = "INSERT INTO [proker_journal] VALUES(@pjid, @pkid, @agid, @st, @nots, null, @tstp)";
|
|
PJUpdate.Parameters.AddWithValue("@pjid", GenerateUuidV7(Now));
|
|
PJUpdate.Parameters.AddWithValue("@pkid", PKID);
|
|
PJUpdate.Parameters.AddWithValue("@agid", CurrUser.AgentID);
|
|
PJUpdate.Parameters.AddWithValue("@st", Status);
|
|
PJUpdate.Parameters.AddWithValue("@nots", Notes);
|
|
PJUpdate.Parameters.AddWithValue("@tstp", Now);
|
|
_ = await PJUpdate.ExecuteNonQueryAsync(CTS.Token);
|
|
}
|
|
}, CTS.Token);
|
|
await runner.WriteJsonResponse(StatusCodes.Status202Accepted, "Proker submission processed.");
|
|
});
|
|
})
|
|
.Map("/submitpk", proker =>
|
|
{
|
|
proker.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3, "POST", true) || !Auth.TryGetUser(runner, out SafeUser CurrUser) ||
|
|
await runner.TryGetBodyJsonAsync(["agentid", "year"], CTS.Token) is not Dictionary<string, JsonElement> InElement) return;
|
|
if (CurrUser.Level != 3)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status401Unauthorized, "Only Level 3 Users may submit their own Prokers.");
|
|
return;
|
|
}
|
|
if (
|
|
InElement["agentid"].GetString() is not string AgentID || AgentID.Equals(string.Empty) ||
|
|
InElement["year"].GetInt16() is short Year && Year < 2020
|
|
)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "Required property values is invalid or out of allowed range.");
|
|
return;
|
|
}
|
|
List<string> PKIDList = [];
|
|
await RunTransactionAsync(CS, async (Conn, Trans) =>
|
|
{
|
|
using (SqlCommand PKIDFetch = Conn.CreateCommand())
|
|
{
|
|
PKIDFetch.Transaction = Trans;
|
|
PKIDFetch.CommandText = "SELECT [prokerid] FROM [LatestJournal] WHERE [ownerid] = @oi AND [year] = @yr AND [status] = 0 OR [status] = 1 ORDER BY [kegiatan]";
|
|
PKIDFetch.Parameters.AddWithValue("@oi", AgentID);
|
|
PKIDFetch.Parameters.AddWithValue("@yr", Year);
|
|
using SqlDataReader PKRd = await PKIDFetch.ExecuteReaderAsync(CTS.Token);
|
|
PKIDList = await PKRd.ToListAsync(PKID => (string)PKID["prokerid"], CTS.Token);
|
|
}
|
|
foreach (string PKID in PKIDList)
|
|
{
|
|
DateTime Now = DateTime.Now;
|
|
using SqlCommand PJUpdate = Conn.CreateCommand();
|
|
PJUpdate.Transaction = Trans;
|
|
PJUpdate.CommandText = "INSERT INTO [proker_journal] VALUES(@pjid, @pkid, @oi, 2, 'Pengajuan Program Kerja', null, @tstp)";
|
|
PJUpdate.Parameters.AddWithValue("@pjid", GenerateUuidV7(Now));
|
|
PJUpdate.Parameters.AddWithValue("@pkid", PKID);
|
|
PJUpdate.Parameters.AddWithValue("@oi", AgentID);
|
|
PJUpdate.Parameters.AddWithValue("@tstp", Now);
|
|
_ = await PJUpdate.ExecuteNonQueryAsync(CTS.Token);
|
|
}
|
|
}, CTS.Token);
|
|
await runner.WriteJsonResponse(StatusCodes.Status202Accepted, "Proker draft submitted.", PKIDList);
|
|
});
|
|
})
|
|
.Map("/pkevidence", proker =>
|
|
{
|
|
proker.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3, "POST", true) || !Auth.TryGetUser(runner, out SafeUser CurrUser) ||
|
|
await runner.TryGetBodyJsonAsync(["prokerid", "notes", "evidence"], CTS.Token) is not Dictionary<string, JsonElement> InElement) return;
|
|
if (
|
|
InElement["prokerid"].GetString() is not string ProkerID || ProkerID.Equals(string.Empty) ||
|
|
InElement["notes"].GetString() is not string Notes || Notes.Equals(string.Empty) ||
|
|
InElement["evidence"].GetString() is not string Evidence || Evidence.Equals(string.Empty)
|
|
)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "Required property values is invalid or out of allowed range.");
|
|
return;
|
|
}
|
|
byte[] PDFBytes = Convert.FromBase64String(Evidence);
|
|
string FileName = $"{Crc32.Compute(PDFBytes):X8}.pdf";
|
|
string FilePath = Path.Combine(AppContext.BaseDirectory, "wwwroot/uploads/dokumen", FileName);
|
|
if (!File.Exists(FilePath)) await File.WriteAllBytesAsync(FilePath, PDFBytes, CTS.Token);
|
|
await RunNonQueryAsync(CS, "INSERT INTO [proker_journal] VALUES (@pjid, @pkid, @owid, 4, @nots, @docs, @tstp)", Query =>
|
|
{
|
|
DateTime Now = DateTime.Now;
|
|
Query.Parameters.AddWithValue("@pjid", GenerateUuidV7(Now));
|
|
Query.Parameters.AddWithValue("@pkid", ProkerID);
|
|
Query.Parameters.AddWithValue("@owid", CurrUser.AgentID);
|
|
Query.Parameters.AddWithValue("@nots", Notes);
|
|
Query.Parameters.AddWithValue("@docs", FileName);
|
|
Query.Parameters.AddWithValue("@tstp", Now);
|
|
}, CTS.Token);
|
|
await runner.WriteJsonResponse(StatusCodes.Status201Created, "Proker evidence accepted. Journal created successfully.");
|
|
});
|
|
})
|
|
.Map("/eval", proker =>
|
|
{
|
|
proker.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(1, "POST", true) || !Auth.TryGetUser(runner, out SafeUser CurrUser) ||
|
|
await runner.TryGetBodyJsonAsync(["prokerid", "notes"], CTS.Token) is not Dictionary<string, JsonElement> InElement) return;
|
|
if (
|
|
InElement["prokerid"].GetString() is not string ProkerID || ProkerID.Equals(string.Empty) ||
|
|
InElement["notes"].GetString() is not string Notes || Notes.Equals(string.Empty)
|
|
)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "Required property values is invalid or out of allowed range.");
|
|
return;
|
|
}
|
|
await RunNonQueryAsync(CS, "INSERT INTO [proker_journal] VALUES (@pjid, @pkid, @owid, 8, @nots, null, @tstp)", Query =>
|
|
{
|
|
DateTime Now = DateTime.Now;
|
|
Query.Parameters.AddWithValue("@pjid", GenerateUuidV7(Now));
|
|
Query.Parameters.AddWithValue("@pkid", ProkerID);
|
|
Query.Parameters.AddWithValue("@owid", CurrUser.AgentID);
|
|
Query.Parameters.AddWithValue("@nots", Notes);
|
|
Query.Parameters.AddWithValue("@tstp", Now);
|
|
}, CTS.Token);
|
|
await runner.WriteJsonResponse(StatusCodes.Status201Created, "Proker evaluation accepted. Journal created successfully.");
|
|
});
|
|
})
|
|
.Map("/regs", regs =>
|
|
{
|
|
regs.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3)) return;
|
|
using SqlDataReader RegRd = await RunReaderAsync(CS, "SELECT * FROM [regulations]", null, CTS.Token);
|
|
List<Regulation> Regulations = await RegRd.ToListAsync<Regulation>(R => new(
|
|
(string)RegRd["id"],
|
|
(string)RegRd["judul"],
|
|
RegRd["abstrak"] == DBNull.Value ? "" : (string)RegRd["abstrak"],
|
|
(string)RegRd["agentid"],
|
|
(DateTime)RegRd["timestamp"]
|
|
));
|
|
await runner.WriteJsonResponse(StatusCodes.Status200OK, "Regulation List fetched.", Regulations);
|
|
});
|
|
})
|
|
.Map("/addreg", reg =>
|
|
{
|
|
reg.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3,"POST",true) || !Auth.TryGetUser(runner, out SafeUser CurrUser) || await runner.TryGetBodyJsonAsync(["judul", "abstrak", "pdf"], CTS.Token) is not Dictionary<string, JsonElement> InElement) return;
|
|
if (
|
|
InElement["judul"].GetString() is not string Judul ||
|
|
InElement["abstrak"].GetString() is not string Abstrak ||
|
|
InElement["pdf"].GetString() is not string InPDF
|
|
)
|
|
{
|
|
await runner.WriteJsonResponse(StatusCodes.Status400BadRequest, "One or more required propertyies are not provided or of invalid format/value.");
|
|
return;
|
|
}
|
|
byte[] PDFBytes = Convert.FromBase64String(InPDF);
|
|
string FileName = $"{Crc32.Compute(PDFBytes):X8}.pdf";
|
|
string FilePath = Path.Combine(AppContext.BaseDirectory, "wwwroot/uploads/regulasi", FileName);
|
|
if (!File.Exists(FilePath)) await File.WriteAllBytesAsync(FilePath, PDFBytes, CTS.Token);
|
|
await RunNonQueryAsync(CS, "INSERT INTO [regulations] VALUES(@id, @jd, @ab, @ag, SYSDATETIME())", Query =>
|
|
{
|
|
Query.Parameters.AddWithValue("@id", FileName);
|
|
Query.Parameters.AddWithValue("@jd", Judul);
|
|
Query.Parameters.AddWithValue("@ab", Abstrak.Length > 1 ? Abstrak : DBNull.Value);
|
|
Query.Parameters.AddWithValue("@ag", CurrUser.AgentID);
|
|
}, CTS.Token);
|
|
await runner.WriteJsonResponse(StatusCodes.Status201Created, "New regulation entry created.");
|
|
});
|
|
})
|
|
.Map("/remreg", reg =>
|
|
{
|
|
reg.Run(async runner =>
|
|
{
|
|
if (!await runner.RequestValidated(3,"POST",true) || await runner.TryGetBodyJsonAsync(["id"], CTS.Token) is not Dictionary<string, JsonElement> InElement || InElement["id"].GetString() is not string ID || ID.Length < 12) return;
|
|
await RunNonQueryAsync(CS, "DELETE FROM [regulations] WHERE id = @id", Query =>
|
|
{
|
|
Query.Parameters.AddWithValue("@id", ID);
|
|
}, CTS.Token);
|
|
await runner.WriteJsonResponse(StatusCodes.Status200OK, "Regulation entry removed.");
|
|
});
|
|
})
|
|
;
|
|
}
|
|
|
|
}
|