Add basic API, only with system endpoints
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
using NodaTime;
|
||||
using Npgsql;
|
||||
|
||||
namespace PluralKit
|
||||
{
|
||||
public static class DatabaseUtils
|
||||
{
|
||||
public static void Init()
|
||||
{
|
||||
// Dapper by default tries to pass ulongs to Npgsql, which rejects them since PostgreSQL technically
|
||||
// doesn't support unsigned types on its own.
|
||||
// Instead we add a custom mapper to encode them as signed integers instead, converting them back and forth.
|
||||
SqlMapper.RemoveTypeMap(typeof(ulong));
|
||||
SqlMapper.AddTypeHandler<ulong>(new UlongEncodeAsLongHandler());
|
||||
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
|
||||
|
||||
// Also, use NodaTime. it's good.
|
||||
NpgsqlConnection.GlobalTypeMapper.UseNodaTime();
|
||||
// With the thing we add above, Npgsql already handles NodaTime integration
|
||||
// This makes Dapper confused since it thinks it has to convert it anyway and doesn't understand the types
|
||||
// So we add a custom type handler that literally just passes the type through to Npgsql
|
||||
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<Instant>());
|
||||
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<LocalDate>());
|
||||
}
|
||||
|
||||
class UlongEncodeAsLongHandler : SqlMapper.TypeHandler<ulong>
|
||||
{
|
||||
public override ulong Parse(object value)
|
||||
{
|
||||
// Cast to long to unbox, then to ulong (???)
|
||||
return (ulong)(long)value;
|
||||
}
|
||||
|
||||
public override void SetValue(IDbDataParameter parameter, ulong value)
|
||||
{
|
||||
parameter.Value = (long)value;
|
||||
}
|
||||
}
|
||||
|
||||
class PassthroughTypeHandler<T> : SqlMapper.TypeHandler<T>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, T value)
|
||||
{
|
||||
parameter.Value = value;
|
||||
}
|
||||
|
||||
public override T Parse(object value)
|
||||
{
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using Dapper.Contrib.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using NodaTime;
|
||||
using NodaTime.Text;
|
||||
|
||||
@@ -6,39 +7,38 @@ namespace PluralKit
|
||||
{
|
||||
public class PKSystem
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
public string Hid { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Tag { get; set; }
|
||||
public string AvatarUrl { get; set; }
|
||||
public string Token { get; set; }
|
||||
public Instant Created { get; set; }
|
||||
public string UiTz { get; set; }
|
||||
[Key] [JsonIgnore] public int Id { get; set; }
|
||||
[JsonProperty("id")] public string Hid { get; set; }
|
||||
[JsonProperty("name")] public string Name { get; set; }
|
||||
[JsonProperty("description")] public string Description { get; set; }
|
||||
[JsonProperty("tag")] public string Tag { get; set; }
|
||||
[JsonProperty("avatar_url")] public string AvatarUrl { get; set; }
|
||||
[JsonIgnore] public string Token { get; set; }
|
||||
[JsonProperty("created")] public Instant Created { get; set; }
|
||||
[JsonProperty("tz")] public string UiTz { get; set; }
|
||||
|
||||
public int MaxMemberNameLength => Tag != null ? 32 - Tag.Length - 1 : 32;
|
||||
[JsonIgnore] public int MaxMemberNameLength => Tag != null ? 32 - Tag.Length - 1 : 32;
|
||||
|
||||
public DateTimeZone Zone => DateTimeZoneProviders.Tzdb.GetZoneOrNull(UiTz);
|
||||
[JsonIgnore] public DateTimeZone Zone => DateTimeZoneProviders.Tzdb.GetZoneOrNull(UiTz);
|
||||
}
|
||||
|
||||
public class PKMember
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Hid { get; set; }
|
||||
public int System { get; set; }
|
||||
public string Color { get; set; }
|
||||
public string AvatarUrl { get; set; }
|
||||
public string Name { get; set; }
|
||||
public LocalDate? Birthday { get; set; }
|
||||
public string Pronouns { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Prefix { get; set; }
|
||||
public string Suffix { get; set; }
|
||||
public Instant Created { get; set; }
|
||||
[JsonIgnore] public int Id { get; set; }
|
||||
[JsonProperty("id")] public string Hid { get; set; }
|
||||
[JsonIgnore] public int System { get; set; }
|
||||
[JsonProperty("color")] public string Color { get; set; }
|
||||
[JsonProperty("avatar_url")] public string AvatarUrl { get; set; }
|
||||
[JsonProperty("name")] public string Name { get; set; }
|
||||
[JsonProperty("birthday")] public LocalDate? Birthday { get; set; }
|
||||
[JsonProperty("pronouns")] public string Pronouns { get; set; }
|
||||
[JsonProperty("description")] public string Description { get; set; }
|
||||
[JsonProperty("prefix")] public string Prefix { get; set; }
|
||||
[JsonProperty("suffix")] public string Suffix { get; set; }
|
||||
[JsonProperty("created")] public Instant Created { get; set; }
|
||||
|
||||
/// Returns a formatted string representing the member's birthday, taking into account that a year of "0001" is hidden
|
||||
public string BirthdayString
|
||||
[JsonIgnore] public string BirthdayString
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -50,8 +50,8 @@ namespace PluralKit
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasProxyTags => Prefix != null || Suffix != null;
|
||||
public string ProxyString => $"{Prefix ?? ""}text{Suffix ?? ""}";
|
||||
[JsonIgnore] public bool HasProxyTags => Prefix != null || Suffix != null;
|
||||
[JsonIgnore] public string ProxyString => $"{Prefix ?? ""}text{Suffix ?? ""}";
|
||||
}
|
||||
|
||||
public class PKSwitch
|
||||
|
@@ -13,8 +13,15 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.0-alpha01" />
|
||||
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="2.2.0" />
|
||||
<PackageReference Include="Npgsql" Version="4.0.6" />
|
||||
<PackageReference Include="Npgsql.NodaTime" Version="4.0.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="db_schema.sql" />
|
||||
<EmbeddedResource Include="db_schema.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@@ -1,64 +1,19 @@
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
|
||||
namespace PluralKit {
|
||||
public static class Schema {
|
||||
public static async Task CreateTables(IDbConnection connection) {
|
||||
await connection.ExecuteAsync(@"create table if not exists systems (
|
||||
id serial primary key,
|
||||
hid char(5) unique not null,
|
||||
name text,
|
||||
description text,
|
||||
tag text,
|
||||
avatar_url text,
|
||||
token text,
|
||||
created timestamp not null default (current_timestamp at time zone 'utc'),
|
||||
ui_tz text not null default 'UTC'
|
||||
)");
|
||||
await connection.ExecuteAsync(@"create table if not exists members (
|
||||
id serial primary key,
|
||||
hid char(5) unique not null,
|
||||
system serial not null references systems(id) on delete cascade,
|
||||
color char(6),
|
||||
avatar_url text,
|
||||
name text not null,
|
||||
birthday date,
|
||||
pronouns text,
|
||||
description text,
|
||||
prefix text,
|
||||
suffix text,
|
||||
created timestamp not null default (current_timestamp at time zone 'utc')
|
||||
)");
|
||||
await connection.ExecuteAsync(@"create table if not exists accounts (
|
||||
uid bigint primary key,
|
||||
system serial not null references systems(id) on delete cascade
|
||||
)");
|
||||
await connection.ExecuteAsync(@"create table if not exists messages (
|
||||
mid bigint primary key,
|
||||
channel bigint not null,
|
||||
member serial not null references members(id) on delete cascade,
|
||||
sender bigint not null
|
||||
)");
|
||||
await connection.ExecuteAsync(@"create table if not exists switches (
|
||||
id serial primary key,
|
||||
system serial not null references systems(id) on delete cascade,
|
||||
timestamp timestamp not null default (current_timestamp at time zone 'utc')
|
||||
)");
|
||||
await connection.ExecuteAsync(@"create table if not exists switch_members (
|
||||
id serial primary key,
|
||||
switch serial not null references switches(id) on delete cascade,
|
||||
member serial not null references members(id) on delete cascade
|
||||
)");
|
||||
await connection.ExecuteAsync(@"create table if not exists webhooks (
|
||||
channel bigint primary key,
|
||||
webhook bigint not null,
|
||||
token text not null
|
||||
)");
|
||||
await connection.ExecuteAsync(@"create table if not exists servers (
|
||||
id bigint primary key,
|
||||
log_channel bigint
|
||||
)");
|
||||
public static async Task CreateTables(IDbConnection connection)
|
||||
{
|
||||
// Load the schema from disk (well, embedded resource) and execute the commands in there
|
||||
using (var stream = typeof(Schema).Assembly.GetManifestResourceStream("PluralKit.Core.db_schema.sql"))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
var result = await reader.ReadToEndAsync();
|
||||
await connection.ExecuteAsync(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,10 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
using NodaTime;
|
||||
using NodaTime.Serialization.JsonNet;
|
||||
using NodaTime.Text;
|
||||
using Npgsql;
|
||||
|
||||
|
||||
namespace PluralKit
|
||||
@@ -258,4 +265,70 @@ namespace PluralKit
|
||||
public static IPattern<LocalDateTime> LocalDateTimeFormat = LocalDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss");
|
||||
public static IPattern<ZonedDateTime> ZonedDateTimeFormat = ZonedDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss x", DateTimeZoneProviders.Tzdb);
|
||||
}
|
||||
public static class InitUtils
|
||||
{
|
||||
public static IConfigurationBuilder BuildConfiguration(string[] args) => new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("pluralkit.conf", true)
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args);
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
InitDatabase();
|
||||
}
|
||||
|
||||
private static void InitDatabase()
|
||||
{
|
||||
// Dapper by default tries to pass ulongs to Npgsql, which rejects them since PostgreSQL technically
|
||||
// doesn't support unsigned types on its own.
|
||||
// Instead we add a custom mapper to encode them as signed integers instead, converting them back and forth.
|
||||
SqlMapper.RemoveTypeMap(typeof(ulong));
|
||||
SqlMapper.AddTypeHandler<ulong>(new UlongEncodeAsLongHandler());
|
||||
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
|
||||
|
||||
// Also, use NodaTime. it's good.
|
||||
NpgsqlConnection.GlobalTypeMapper.UseNodaTime();
|
||||
// With the thing we add above, Npgsql already handles NodaTime integration
|
||||
// This makes Dapper confused since it thinks it has to convert it anyway and doesn't understand the types
|
||||
// So we add a custom type handler that literally just passes the type through to Npgsql
|
||||
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<Instant>());
|
||||
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<LocalDate>());
|
||||
}
|
||||
|
||||
public static JsonSerializerSettings BuildSerializerSettings() => new JsonSerializerSettings().BuildSerializerSettings();
|
||||
|
||||
public static JsonSerializerSettings BuildSerializerSettings(this JsonSerializerSettings settings)
|
||||
{
|
||||
settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
public class UlongEncodeAsLongHandler : SqlMapper.TypeHandler<ulong>
|
||||
{
|
||||
public override ulong Parse(object value)
|
||||
{
|
||||
// Cast to long to unbox, then to ulong (???)
|
||||
return (ulong)(long)value;
|
||||
}
|
||||
|
||||
public override void SetValue(IDbDataParameter parameter, ulong value)
|
||||
{
|
||||
parameter.Value = (long)value;
|
||||
}
|
||||
}
|
||||
|
||||
public class PassthroughTypeHandler<T> : SqlMapper.TypeHandler<T>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, T value)
|
||||
{
|
||||
parameter.Value = value;
|
||||
}
|
||||
|
||||
public override T Parse(object value)
|
||||
{
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
69
PluralKit.Core/db_schema.sql
Normal file
69
PluralKit.Core/db_schema.sql
Normal file
@@ -0,0 +1,69 @@
|
||||
create table if not exists systems
|
||||
(
|
||||
id serial primary key,
|
||||
hid char(5) unique not null,
|
||||
name text,
|
||||
description text,
|
||||
tag text,
|
||||
avatar_url text,
|
||||
token text,
|
||||
created timestamp not null default (current_timestamp at time zone 'utc'),
|
||||
ui_tz text not null default 'UTC'
|
||||
);
|
||||
|
||||
create table if not exists members
|
||||
(
|
||||
id serial primary key,
|
||||
hid char(5) unique not null,
|
||||
system serial not null references systems (id) on delete cascade,
|
||||
color char(6),
|
||||
avatar_url text,
|
||||
name text not null,
|
||||
birthday date,
|
||||
pronouns text,
|
||||
description text,
|
||||
prefix text,
|
||||
suffix text,
|
||||
created timestamp not null default (current_timestamp at time zone 'utc')
|
||||
);
|
||||
|
||||
create table if not exists accounts
|
||||
(
|
||||
uid bigint primary key,
|
||||
system serial not null references systems (id) on delete cascade
|
||||
);
|
||||
|
||||
create table if not exists messages
|
||||
(
|
||||
mid bigint primary key,
|
||||
channel bigint not null,
|
||||
member serial not null references members (id) on delete cascade,
|
||||
sender bigint not null
|
||||
);
|
||||
|
||||
create table if not exists switches
|
||||
(
|
||||
id serial primary key,
|
||||
system serial not null references systems (id) on delete cascade,
|
||||
timestamp timestamp not null default (current_timestamp at time zone 'utc')
|
||||
);
|
||||
|
||||
create table if not exists switch_members
|
||||
(
|
||||
id serial primary key,
|
||||
switch serial not null references switches (id) on delete cascade,
|
||||
member serial not null references members (id) on delete cascade
|
||||
);
|
||||
|
||||
create table if not exists webhooks
|
||||
(
|
||||
channel bigint primary key,
|
||||
webhook bigint not null,
|
||||
token text not null
|
||||
);
|
||||
|
||||
create table if not exists servers
|
||||
(
|
||||
id bigint primary key,
|
||||
log_channel bigint
|
||||
);
|
Reference in New Issue
Block a user