From 8e74d2adcac1ff231abcd138aa1f1eefc1a519e0 Mon Sep 17 00:00:00 2001 From: Jay Malhotra <5047192+SapiensAnatis@users.noreply.github.com> Date: Sun, 26 May 2024 01:35:46 +0100 Subject: [PATCH] Svelte website: login and logout (#835) - Implement login button and baas auth - Reserve /api for website data endpoints. Example included with functional validation of idToken cookie, but not used yet --- Directory.Packages.props | 147 ++++++++-------- .../DatabaseConfiguration.cs | 22 ++- .../TestFixture.cs | 5 +- DragaliaAPI/DragaliaAPI/DragaliaAPI.csproj | 3 + .../Features/Web/ConfigureJwtBearerOptions.cs | 29 ++++ .../Features/Web/FeatureExtensions.cs | 43 +++++ .../DragaliaAPI/Features/Web/UserQuery.cs | 19 +++ .../DragaliaAPI/Features/Web/UserService.cs | 21 +++ .../Features/Web/WebAuthenticationHandler.cs | 59 +++++++ DragaliaAPI/DragaliaAPI/Program.cs | 26 ++- .../DragaliaAPI/ServiceConfiguration.cs | 32 ++-- Website/.env | 5 + Website/.env.development | 3 + Website/.gitignore | 4 +- Website/.prettierrc | 27 +-- Website/components.json | 26 +-- Website/eslint.config.js | 69 ++++---- Website/package.json | 129 +++++++------- Website/playwright.config.ts | 12 +- Website/pnpm-lock.yaml | 160 +++++++++--------- Website/postcss.config.cjs | 12 +- Website/src/app.d.ts | 14 +- Website/src/app.html | 20 +-- Website/src/app.pcss | 114 ++++++------- Website/src/hooks.server.ts | 18 ++ Website/src/lib/components/routes.svelte | 20 +-- Website/src/lib/components/typography.svelte | 16 +- Website/src/lib/cookies.ts | 6 + Website/src/lib/jwt.ts | 44 +++++ Website/src/lib/routes.ts | 24 +-- Website/src/routes/(main)/(home)/+page.svelte | 142 ++++++++-------- .../(main)/(home)/acknowledgement.svelte | 14 +- .../(main)/(home)/icons/buyMeACoffee.svelte | 6 +- .../routes/(main)/(home)/icons/discord.svelte | 6 +- .../routes/(main)/(home)/icons/github.svelte | 10 +- .../routes/(main)/(home)/icons/patreon.svelte | 28 +-- .../routes/(main)/(home)/linkButton.svelte | 16 +- Website/src/routes/(main)/+layout.server.ts | 25 +++ Website/src/routes/(main)/+layout.svelte | 13 +- Website/src/routes/(main)/header.svelte | 129 +++++++------- .../src/routes/(main)/headerContents.svelte | 34 ++++ .../src/routes/(main)/login/+page.server.ts | 40 +++++ .../src/routes/(main)/logout/+page.server.ts | 9 + .../routes/(main)/navigation/+page@.svelte | 6 +- Website/src/routes/(main)/news/+page.svelte | 0 .../src/routes/(main)/oauth/+page.server.ts | 115 +++++++++++++ Website/src/routes/(main)/sideNav.svelte | 26 +-- Website/src/routes/(main)/user.ts | 8 + Website/src/routes/+layout.svelte | 2 +- Website/svelte.config.js | 26 +-- Website/tailwind.config.js | 124 +++++++------- Website/tests/test.ts | 4 +- Website/tsconfig.json | 32 ++-- Website/vite.config.ts | 14 +- 54 files changed, 1232 insertions(+), 726 deletions(-) create mode 100644 DragaliaAPI/DragaliaAPI/Features/Web/ConfigureJwtBearerOptions.cs create mode 100644 DragaliaAPI/DragaliaAPI/Features/Web/FeatureExtensions.cs create mode 100644 DragaliaAPI/DragaliaAPI/Features/Web/UserQuery.cs create mode 100644 DragaliaAPI/DragaliaAPI/Features/Web/UserService.cs create mode 100644 DragaliaAPI/DragaliaAPI/Features/Web/WebAuthenticationHandler.cs create mode 100644 Website/.env create mode 100644 Website/.env.development create mode 100644 Website/src/hooks.server.ts create mode 100644 Website/src/lib/cookies.ts create mode 100644 Website/src/lib/jwt.ts create mode 100644 Website/src/routes/(main)/+layout.server.ts create mode 100644 Website/src/routes/(main)/headerContents.svelte create mode 100644 Website/src/routes/(main)/login/+page.server.ts create mode 100644 Website/src/routes/(main)/logout/+page.server.ts create mode 100644 Website/src/routes/(main)/news/+page.svelte create mode 100644 Website/src/routes/(main)/oauth/+page.server.ts create mode 100644 Website/src/routes/(main)/user.ts diff --git a/Directory.Packages.props b/Directory.Packages.props index 3d88ca62b..37f6ae815 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,77 +3,80 @@ true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DragaliaAPI/DragaliaAPI.Database/DatabaseConfiguration.cs b/DragaliaAPI/DragaliaAPI.Database/DatabaseConfiguration.cs index bf9d2dbd5..548ddc63b 100644 --- a/DragaliaAPI/DragaliaAPI.Database/DatabaseConfiguration.cs +++ b/DragaliaAPI/DragaliaAPI.Database/DatabaseConfiguration.cs @@ -3,8 +3,10 @@ using DragaliaAPI.Database.Repositories; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; [assembly: InternalsVisibleTo("DragaliaAPI.Database.Test")] [assembly: InternalsVisibleTo("DragaliaAPI.Test")] @@ -18,14 +20,22 @@ public static class DatabaseConfiguration public static IServiceCollection ConfigureDatabaseServices( this IServiceCollection services, - PostgresOptions postgresOptions + IConfiguration config ) { - services = services - .AddDbContext(options => - options - .UseNpgsql(postgresOptions.GetConnectionString("ApiContext")) - .EnableDetailedErrors() + services.Configure(config.GetRequiredSection(nameof(PostgresOptions))); + + services + .AddDbContext( + (serviceProvider, options) => + { + PostgresOptions postgresOptions = serviceProvider + .GetRequiredService>() + .Value; + options + .UseNpgsql(postgresOptions.GetConnectionString("ApiContext")) + .EnableDetailedErrors(); + } ) #pragma warning disable CS0618 // Type or member is obsolete .AddScoped() diff --git a/DragaliaAPI/DragaliaAPI.Integration.Test/TestFixture.cs b/DragaliaAPI/DragaliaAPI.Integration.Test/TestFixture.cs index eb671ee30..9e7283c0a 100644 --- a/DragaliaAPI/DragaliaAPI.Integration.Test/TestFixture.cs +++ b/DragaliaAPI/DragaliaAPI.Integration.Test/TestFixture.cs @@ -182,7 +182,10 @@ protected HttpClient CreateClient(Action? extraBuilderConfig = .CreateClient( new WebApplicationFactoryClientOptions() { - BaseAddress = new Uri("http://localhost/api/", UriKind.Absolute), + BaseAddress = new Uri( + "http://localhost/2.19.0_20220714193707/", + UriKind.Absolute + ), } ); diff --git a/DragaliaAPI/DragaliaAPI/DragaliaAPI.csproj b/DragaliaAPI/DragaliaAPI/DragaliaAPI.csproj index 58f3562ac..8a201535b 100644 --- a/DragaliaAPI/DragaliaAPI/DragaliaAPI.csproj +++ b/DragaliaAPI/DragaliaAPI/DragaliaAPI.csproj @@ -18,6 +18,8 @@ + + @@ -25,6 +27,7 @@ all build; native; contentfiles; analyzers; buildtransitive + diff --git a/DragaliaAPI/DragaliaAPI/Features/Web/ConfigureJwtBearerOptions.cs b/DragaliaAPI/DragaliaAPI/Features/Web/ConfigureJwtBearerOptions.cs new file mode 100644 index 000000000..c5256ce6a --- /dev/null +++ b/DragaliaAPI/DragaliaAPI/Features/Web/ConfigureJwtBearerOptions.cs @@ -0,0 +1,29 @@ +using DragaliaAPI.Models.Options; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Options; + +namespace DragaliaAPI.Features.Web; + +/// +/// Allows setting additional properties on using DI. +/// +/// +/// Sourced from: +/// +public class ConfigureJwtBearerOptions(IOptions baasOptions) + : IConfigureNamedOptions +{ + // Never called + public void Configure(JwtBearerOptions options) => + this.Configure(JwtBearerDefaults.AuthenticationScheme, options); + + public void Configure(string? name, JwtBearerOptions options) + { + options.Authority = baasOptions.Value.BaasUrl; + options.TokenValidationParameters = new() + { + ValidAudience = baasOptions.Value.TokenAudience, + ValidIssuer = baasOptions.Value.TokenIssuer, + }; + } +} diff --git a/DragaliaAPI/DragaliaAPI/Features/Web/FeatureExtensions.cs b/DragaliaAPI/DragaliaAPI/Features/Web/FeatureExtensions.cs new file mode 100644 index 000000000..e70f2ec03 --- /dev/null +++ b/DragaliaAPI/DragaliaAPI/Features/Web/FeatureExtensions.cs @@ -0,0 +1,43 @@ +using DragaliaAPI.Features.Web; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Options; + +// ReSharper disable once CheckNamespace +namespace DragaliaAPI; + +public static partial class FeatureExtensions +{ + public static IServiceCollection AddWebFeature(this IServiceCollection serviceCollection) + { + serviceCollection + .AddTransient, ConfigureJwtBearerOptions>() + .AddScoped(); + + serviceCollection + .AddAuthentication() + .AddJwtBearer( + WebAuthenticationHelper.SchemeName, + opts => + { + opts.Events = new() + { + OnMessageReceived = WebAuthenticationHelper.OnMessageReceived, + OnTokenValidated = WebAuthenticationHelper.OnTokenValidated + }; + // The rest is configured in ConfigureJwtBearerOptions.cs after the ServiceProvider is built. + } + ); + + serviceCollection + .AddAuthorizationBuilder() + .AddPolicy( + WebAuthenticationHelper.PolicyName, + builder => + builder + .RequireAuthenticatedUser() + .AddAuthenticationSchemes(WebAuthenticationHelper.SchemeName) + ); + + return serviceCollection; + } +} diff --git a/DragaliaAPI/DragaliaAPI/Features/Web/UserQuery.cs b/DragaliaAPI/DragaliaAPI/Features/Web/UserQuery.cs new file mode 100644 index 000000000..6cc83b899 --- /dev/null +++ b/DragaliaAPI/DragaliaAPI/Features/Web/UserQuery.cs @@ -0,0 +1,19 @@ +using Immediate.Apis.Shared; +using Immediate.Handlers.Shared; +using Microsoft.AspNetCore.Authorization; + +namespace DragaliaAPI.Features.Web; + +[Handler] +[MapGet("/api/user")] +[Authorize(WebAuthenticationHelper.PolicyName)] +public static partial class UserQuery +{ + public record Query; + + private static async ValueTask HandleAsync( + Query _, + UserService userService, + CancellationToken cancellationToken + ) => await userService.GetUser(cancellationToken); +} diff --git a/DragaliaAPI/DragaliaAPI/Features/Web/UserService.cs b/DragaliaAPI/DragaliaAPI/Features/Web/UserService.cs new file mode 100644 index 000000000..087367c12 --- /dev/null +++ b/DragaliaAPI/DragaliaAPI/Features/Web/UserService.cs @@ -0,0 +1,21 @@ +using DragaliaAPI.Database; +using DragaliaAPI.Shared.PlayerDetails; +using Microsoft.EntityFrameworkCore; + +namespace DragaliaAPI.Features.Web; + +public class UserService(IPlayerIdentityService playerIdentityService, ApiContext apiContext) +{ + public Task GetUser(CancellationToken cancellationToken) => + apiContext + .Players.Where(x => x.ViewerId == playerIdentityService.ViewerId) + .Select(x => new User() { Name = x.UserData!.Name, ViewerId = x.ViewerId, }) + .FirstAsync(cancellationToken); +} + +public class User +{ + public long ViewerId { get; init; } + + public required string Name { get; init; } +} diff --git a/DragaliaAPI/DragaliaAPI/Features/Web/WebAuthenticationHandler.cs b/DragaliaAPI/DragaliaAPI/Features/Web/WebAuthenticationHandler.cs new file mode 100644 index 000000000..2a2b54ef5 --- /dev/null +++ b/DragaliaAPI/DragaliaAPI/Features/Web/WebAuthenticationHandler.cs @@ -0,0 +1,59 @@ +using System.Diagnostics; +using System.IdentityModel.Tokens.Jwt; +using System.Text.Encodings.Web; +using DragaliaAPI.Database; +using DragaliaAPI.Models.Options; +using DragaliaAPI.Services.Api; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Tokens; + +namespace DragaliaAPI.Features.Web; + +public static class WebAuthenticationHelper +{ + public const string SchemeName = "WebScheme"; + + public const string PolicyName = "WebPolicy"; + + public static Task OnMessageReceived(MessageReceivedContext context) + { + if (context.Request.Cookies.TryGetValue("idToken", out string? idToken)) + { + context.Token = idToken; + } + + return Task.CompletedTask; + } + + public static async Task OnTokenValidated(TokenValidatedContext context) + { + if (context.SecurityToken is not JsonWebToken jsonWebToken) + { + throw new UnreachableException("What the fuck?"); + } + + string accountId = jsonWebToken.Subject; + + ApiContext dbContext = context.HttpContext.RequestServices.GetRequiredService(); + + var playerInfo = await dbContext + .Players.IgnoreQueryFilters() + .Where(x => x.AccountId == accountId) + .Select(x => new + { + x.ViewerId, + x.UserData!.LastSaveImportTime, + x.UserData.Name, + }) + .FirstOrDefaultAsync(); + + if (playerInfo is null) + { + context.Fail("Unknown player"); + } + } +} diff --git a/DragaliaAPI/DragaliaAPI/Program.cs b/DragaliaAPI/DragaliaAPI/Program.cs index aec5555a6..72df3301a 100644 --- a/DragaliaAPI/DragaliaAPI/Program.cs +++ b/DragaliaAPI/DragaliaAPI/Program.cs @@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; using Microsoft.JSInterop; using Serilog; @@ -63,9 +64,6 @@ option.InputFormatters.Add(new CustomMessagePackInputFormatter(CustomResolver.Options)); }); -PostgresOptions postgresOptions = - builder.Configuration.GetSection(nameof(PostgresOptions)).Get() - ?? throw new InvalidOperationException("Failed to get PostgreSQL config"); RedisOptions redisOptions = builder.Configuration.GetSection(nameof(RedisOptions)).Get() ?? throw new InvalidOperationException("Failed to get Redis config"); @@ -73,7 +71,7 @@ builder.Configuration.GetSection(nameof(HangfireOptions)).Get() ?? new() { Enabled = false }; -builder.Services.ConfigureDatabaseServices(postgresOptions); +builder.Services.ConfigureDatabaseServices(builder.Configuration); builder.Services.AddStackExchangeRedisCache(options => { options.ConfigurationOptions = new() @@ -86,7 +84,7 @@ if (hangfireOptions.Enabled) { - builder.Services.ConfigureHangfire(postgresOptions); + builder.Services.ConfigureHangfire(); } builder.Services.AddDataProtection().PersistKeysToDbContext(); @@ -116,6 +114,10 @@ app.Logger.LogInformation("Loaded MasterAsset in {Time} ms.", watch.ElapsedMilliseconds); +PostgresOptions postgresOptions = app + .Services.GetRequiredService>() + .Value; + app.Logger.LogDebug( "Using PostgreSQL connection {Host}:{Port}", postgresOptions.Hostname, @@ -138,7 +140,6 @@ #pragma warning disable CA1861 // Avoid constant arrays as arguments. Only created once as top-level statement. FrozenSet apiRoutePrefixes = new[] { - "/api", "/2.19.0_20220714193707", "/2.19.0_20220719103923" }.ToFrozenSet(); @@ -171,10 +172,18 @@ ); app.MapWhen( - ctx => !apiRoutePrefixes.Any(prefix => ctx.Request.Path.StartsWithSegments(prefix)), + static ctx => ctx.Request.Path.StartsWithSegments("/api"), applicationBuilder => { + // todo unfuck cors + applicationBuilder.UseCors(cors => + cors.WithOrigins("http://localhost:3001") + .AllowCredentials() + .AllowAnyHeader() + .AllowAnyMethod() + ); applicationBuilder.UseRouting(); + applicationBuilder.UseSerilogRequestLogging(); #pragma warning disable ASP0001 applicationBuilder.UseAuthorization(); #pragma warning restore ASP0001 @@ -182,8 +191,7 @@ applicationBuilder.UseMiddleware(); applicationBuilder.UseEndpoints(endpoints => { - endpoints.MapRazorPages(); - endpoints.MapRazorComponents().AddInteractiveServerRenderMode(); + endpoints.MapDragaliaAPIEndpoints(); }); } ); diff --git a/DragaliaAPI/DragaliaAPI/ServiceConfiguration.cs b/DragaliaAPI/DragaliaAPI/ServiceConfiguration.cs index d4c62ab2f..4e24dda09 100644 --- a/DragaliaAPI/DragaliaAPI/ServiceConfiguration.cs +++ b/DragaliaAPI/DragaliaAPI/ServiceConfiguration.cs @@ -42,6 +42,7 @@ using Hangfire.PostgreSql; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Options; using MudBlazor; using MudBlazor.Services; @@ -83,7 +84,8 @@ IConfiguration configuration .AddWallFeature() .AddPresentFeature() .AddQuestFeature() - .AddStoryFeature(); + .AddStoryFeature() + .AddWebFeature(); services .RegisterMissionServices() @@ -150,6 +152,8 @@ IConfiguration configuration // Maintenance feature .AddScoped(); + services.AddHandlers(); + services.AddScoped(); services.AddAllOfType(); @@ -259,18 +263,22 @@ public static IServiceCollection ConfigureAuthentication(this IServiceCollection return services; } - public static IServiceCollection ConfigureHangfire( - this IServiceCollection serviceCollection, - PostgresOptions postgresOptions - ) + public static IServiceCollection ConfigureHangfire(this IServiceCollection serviceCollection) { - serviceCollection.AddHangfire(cfg => - cfg.SetDataCompatibilityLevel(CompatibilityLevel.Version_180) - .UseSimpleAssemblyNameTypeSerializer() - .UseRecommendedSerializerSettings() - .UsePostgreSqlStorage(pgCfg => - pgCfg.UseNpgsqlConnection(postgresOptions.GetConnectionString("Hangfire")) - ) + serviceCollection.AddHangfire( + (serviceProvider, cfg) => + { + PostgresOptions postgresOptions = serviceProvider + .GetRequiredService>() + .Value; + + cfg.SetDataCompatibilityLevel(CompatibilityLevel.Version_180) + .UseSimpleAssemblyNameTypeSerializer() + .UseRecommendedSerializerSettings() + .UsePostgreSqlStorage(pgCfg => + pgCfg.UseNpgsqlConnection(postgresOptions.GetConnectionString("Hangfire")) + ); + } ); serviceCollection.AddHangfireServer(); diff --git a/Website/.env b/Website/.env new file mode 100644 index 000000000..cd30c7b72 --- /dev/null +++ b/Website/.env @@ -0,0 +1,5 @@ +PUBLIC_BAAS_URL=https://baas.lukefz.xyz/ +PUBLIC_BAAS_CLIENT_ID=dawnshard +PUBLIC_DAWNSHARD_URL=https://dawnshard.co.uk/ +PUBLIC_DAWNSHARD_API_URL=https://dawnshard.co.uk/api/ +DAWNSHARD_API_URL_SSR=http://10.0.0.2:1234 \ No newline at end of file diff --git a/Website/.env.development b/Website/.env.development new file mode 100644 index 000000000..ac5e672c8 --- /dev/null +++ b/Website/.env.development @@ -0,0 +1,3 @@ +PUBLIC_DAWNSHARD_URL=http://localhost:3001/ +PUBLIC_DAWNSHARD_API_URL=http://localhost:3001/api/ # use Vite proxy +DAWNSHARD_API_URL_SSR=http://localhost:5000/ diff --git a/Website/.gitignore b/Website/.gitignore index 6635cf554..2461d1051 100644 --- a/Website/.gitignore +++ b/Website/.gitignore @@ -3,8 +3,6 @@ node_modules /build /.svelte-kit /package -.env -.env.* -!.env.example +.env*local vite.config.js.timestamp-* vite.config.ts.timestamp-* diff --git a/Website/.prettierrc b/Website/.prettierrc index 7ebb855b9..5dd8d1a28 100644 --- a/Website/.prettierrc +++ b/Website/.prettierrc @@ -1,15 +1,16 @@ { - "useTabs": true, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 100, - "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], - "overrides": [ - { - "files": "*.svelte", - "options": { - "parser": "svelte" - } - } - ] + "useTabs": false, + "singleQuote": true, + "trailingComma": "none", + "tabWidth": 2, + "printWidth": 100, + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] } diff --git a/Website/components.json b/Website/components.json index dbb6df9be..56b9df22d 100644 --- a/Website/components.json +++ b/Website/components.json @@ -1,15 +1,15 @@ { - "$schema": "https://shadcn-svelte.com/schema.json", - "style": "default", - "tailwind": { - "config": "tailwind.config.js", - "css": "src/app.pcss", - "baseColor": "slate", - "cssVariables": true - }, - "aliases": { - "components": "$lib/shadcn/components/", - "utils": "$lib/shadcn/utils.js" - }, - "typescript": true + "$schema": "https://shadcn-svelte.com/schema.json", + "style": "default", + "tailwind": { + "config": "tailwind.config.js", + "css": "src/app.pcss", + "baseColor": "slate", + "cssVariables": true + }, + "aliases": { + "components": "$lib/shadcn/components/", + "utils": "$lib/shadcn/utils.js" + }, + "typescript": true } diff --git a/Website/eslint.config.js b/Website/eslint.config.js index 92263c4d4..322705daf 100644 --- a/Website/eslint.config.js +++ b/Website/eslint.config.js @@ -7,39 +7,38 @@ import globals from 'globals'; /** @type { import("eslint").Linter.Config } */ export default [ - js.configs.recommended, - ...tsEslint.configs.recommended, - ...eslintPluginSvelte.configs['flat/recommended'], - eslintConfigPrettier, - ...eslintPluginSvelte.configs['flat/prettier'], - { - files: ['**/*.svelte'], - languageOptions: { - ecmaVersion: 2022, - sourceType: 'module', - globals: { ...globals.node, ...globals.browser }, - parser: svelteParser, - parserOptions: { - parser: tsEslint.parser, - extraFileExtensions: ['.svelte'] - } - } - }, - { - ignores: [ - '.DS_Store', - 'node_modules', - 'build', - '.svelte-kit', - 'package', - '.env', - '.env.*', - '!.env.example', - 'pnpm-lock.yaml', - 'package-lock.json', - 'postcss.config.cjs', - 'yarn.lock', - 'src/lib/shadcn' - ] - } + js.configs.recommended, + ...tsEslint.configs.recommended, + ...eslintPluginSvelte.configs['flat/recommended'], + eslintConfigPrettier, + ...eslintPluginSvelte.configs['flat/prettier'], + { + files: ['**/*.svelte'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'module', + globals: { ...globals.node, ...globals.browser }, + parser: svelteParser, + parserOptions: { + parser: tsEslint.parser, + extraFileExtensions: ['.svelte'] + } + } + }, + { + ignores: [ + '.DS_Store', + 'node_modules', + 'build', + '.svelte-kit', + 'package', + '.env', + '.env.*', + 'pnpm-lock.yaml', + 'package-lock.json', + 'postcss.config.cjs', + 'yarn.lock', + 'src/lib/shadcn' + ] + } ]; diff --git a/Website/package.json b/Website/package.json index 3ade0627e..81eed78b4 100644 --- a/Website/package.json +++ b/Website/package.json @@ -1,66 +1,67 @@ { - "name": "dawnshard", - "version": "0.0.1", - "private": true, - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "test": "playwright test", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --check . && eslint .", - "format": "prettier --write .", - "shadcn": "pnpm dlx shadcn-svelte@latest add" - }, - "devDependencies": { - "@eslint/js": "9.2.0", - "@playwright/test": "1.44.0", - "@sveltejs/adapter-auto": "3.2.0", - "@sveltejs/enhanced-img": "0.2.0", - "@sveltejs/kit": "2.5.7", - "@sveltejs/vite-plugin-svelte": "3.1.0", - "@types/eslint": "8.56.10", - "@typescript-eslint/eslint-plugin": "7.8.0", - "@typescript-eslint/parser": "7.8.0", - "autoprefixer": "10.4.19", - "eslint": "9.2.0", - "eslint-config-prettier": "9.1.0", - "eslint-plugin-svelte": "2.38.0", - "postcss": "8.4.38", - "postcss-load-config": "5.1.0", - "prettier": "3.2.5", - "prettier-plugin-svelte": "3.2.3", - "prettier-plugin-tailwindcss": "0.5.14", - "svelte": "4.2.16", - "svelte-check": "3.7.1", - "tailwindcss": "3.4.3", - "tslib": "2.6.2", - "typescript": "5.4.5", - "typescript-eslint": "7.8.0", - "vite": "5.2.11" - }, - "type": "module", - "dependencies": { - "bits-ui": "0.21.7", - "buffer": "6.0.3", - "clsx": "2.1.1", - "globals": "15.1.0", - "lucide-svelte": "0.378.0", - "mode-watcher": "0.3.0", - "tailwind-merge": "2.3.0", - "tailwind-variants": "0.2.1", - "vaul-svelte": "0.3.0" - }, - "pnpm": { - "supportedArchitectures": { - "os": [ - "win32", - "linux" - ], - "cpu": [ - "x64" - ] - } - } + "name": "dawnshard", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "test": "playwright test", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "prettier --check . && eslint .", + "format": "prettier --write .", + "shadcn": "pnpm dlx shadcn-svelte@latest add" + }, + "devDependencies": { + "@eslint/js": "9.2.0", + "@playwright/test": "1.44.0", + "@sveltejs/adapter-auto": "3.2.0", + "@sveltejs/enhanced-img": "0.2.0", + "@sveltejs/kit": "2.5.7", + "@sveltejs/vite-plugin-svelte": "3.1.0", + "@types/eslint": "8.56.10", + "@typescript-eslint/eslint-plugin": "8.0.0-alpha.14", + "@typescript-eslint/parser": "8.0.0-alpha.14", + "autoprefixer": "10.4.19", + "eslint": "9.2.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-svelte": "2.38.0", + "postcss": "8.4.38", + "postcss-load-config": "5.1.0", + "prettier": "3.2.5", + "prettier-plugin-svelte": "3.2.3", + "prettier-plugin-tailwindcss": "0.5.14", + "svelte": "4.2.16", + "svelte-check": "3.7.1", + "tailwindcss": "3.4.3", + "tslib": "2.6.2", + "typescript": "5.4.5", + "typescript-eslint": "8.0.0-alpha.14", + "vite": "5.2.11" + }, + "type": "module", + "dependencies": { + "bits-ui": "0.21.7", + "buffer": "6.0.3", + "clsx": "2.1.1", + "globals": "15.1.0", + "lucide-svelte": "0.378.0", + "mode-watcher": "0.3.0", + "tailwind-merge": "2.3.0", + "tailwind-variants": "0.2.1", + "vaul-svelte": "0.3.0", + "zod": "3.23.8" + }, + "pnpm": { + "supportedArchitectures": { + "os": [ + "win32", + "linux" + ], + "cpu": [ + "x64" + ] + } + } } diff --git a/Website/playwright.config.ts b/Website/playwright.config.ts index 1c5d7a1fd..962f19c38 100644 --- a/Website/playwright.config.ts +++ b/Website/playwright.config.ts @@ -1,12 +1,12 @@ import type { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { - webServer: { - command: 'npm run build && npm run preview', - port: 4173 - }, - testDir: 'tests', - testMatch: /(.+\.)?(test|spec)\.[jt]s/ + webServer: { + command: 'npm run build && npm run preview', + port: 4173 + }, + testDir: 'tests', + testMatch: /(.+\.)?(test|spec)\.[jt]s/ }; export default config; diff --git a/Website/pnpm-lock.yaml b/Website/pnpm-lock.yaml index 64d003f65..4765aca97 100644 --- a/Website/pnpm-lock.yaml +++ b/Website/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: vaul-svelte: specifier: 0.3.0 version: 0.3.0(svelte@4.2.16) + zod: + specifier: 3.23.8 + version: 3.23.8 devDependencies: '@eslint/js': specifier: 9.2.0 @@ -58,11 +61,11 @@ importers: specifier: 8.56.10 version: 8.56.10 '@typescript-eslint/eslint-plugin': - specifier: 7.8.0 - version: 7.8.0(@typescript-eslint/parser@7.8.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5) + specifier: 8.0.0-alpha.14 + version: 8.0.0-alpha.14(@typescript-eslint/parser@8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5) '@typescript-eslint/parser': - specifier: 7.8.0 - version: 7.8.0(eslint@9.2.0)(typescript@5.4.5) + specifier: 8.0.0-alpha.14 + version: 8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5) autoprefixer: specifier: 10.4.19 version: 10.4.19(postcss@8.4.38) @@ -106,8 +109,8 @@ importers: specifier: 5.4.5 version: 5.4.5 typescript-eslint: - specifier: 7.8.0 - version: 7.8.0(eslint@9.2.0)(typescript@5.4.5) + specifier: 8.0.0-alpha.14 + version: 8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5) vite: specifier: 5.2.11 version: 5.2.11(@types/node@20.12.11) @@ -623,66 +626,62 @@ packages: '@types/pug@2.0.10': resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - - '@typescript-eslint/eslint-plugin@7.8.0': - resolution: {integrity: sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/eslint-plugin@8.0.0-alpha.14': + resolution: {integrity: sha512-tfw3zfCg+ynwARhVsuMXKBrmWCtqQ2Cr/cjPAuyKhJGY8t069Lc0Y+F5H7oDLlmm+G54v8lAHkTkw4K/p+PpFQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/parser@7.8.0': - resolution: {integrity: sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/parser@8.0.0-alpha.14': + resolution: {integrity: sha512-fD+DFo6aJJYyX4w712HzmE7QmUkoUvtlsFO/MqmYMeHIe0Pz5JZpJ1aYVbdxctazOb7NoW3p3RQgmpDcLT2pdQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/scope-manager@7.8.0': - resolution: {integrity: sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/scope-manager@8.0.0-alpha.14': + resolution: {integrity: sha512-6EmhoNZzfjd/sZGxichVguWUGCCgT12xyw3ppNZ9bM/m6qQCE66BqudGxzD58UPL4PpN++Y8KqVItax0gNq4BQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@7.8.0': - resolution: {integrity: sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/type-utils@8.0.0-alpha.14': + resolution: {integrity: sha512-F/rtAXWMrFPO49xK0XLw7hYtPVrjj+jRJhJRRcSBWRybcu7rvlEQ/Chk+QXvyp15QuwmMD5jAqNI+Fkbxgc0gQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/types@7.8.0': - resolution: {integrity: sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/types@8.0.0-alpha.14': + resolution: {integrity: sha512-2u0FBQ0usELnbTqZhHN6X8ngJlpCchFTroWFG5nvo0TOoiPYV+5AbGiRb0IWMsLfxSzeDJeasUzByVvOHn1t1A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@7.8.0': - resolution: {integrity: sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/typescript-estree@8.0.0-alpha.14': + resolution: {integrity: sha512-FM0qHSJ4Sqg49wBCcljq//J9V8SJbq3XFmjaWCF8Tk2hIuYkYZp7joXHs0Ld3FnM+9rj84OQTqSq8zczArNMNg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/utils@7.8.0': - resolution: {integrity: sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/utils@8.0.0-alpha.14': + resolution: {integrity: sha512-hiH1uqRVyOPd+ZWqInwRob2s3Cq+p7LTIolvj+x7QJ6CpBCPrEMEPVuBiFibw2/rW+zJGTa3Ggjdpqy8bLb60g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 - '@typescript-eslint/visitor-keys@7.8.0': - resolution: {integrity: sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/visitor-keys@8.0.0-alpha.14': + resolution: {integrity: sha512-LwUhX8+ttlzJWhqLAkiH7E1tX2WJS0zvK0D83w4L9DRl4TRSQBuGtPIM1+GvG90VMix8sjlGaybBzWfNji1cUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1784,11 +1783,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript-eslint@7.8.0: - resolution: {integrity: sha512-sheFG+/D8N/L7gC3WT0Q8sB97Nm573Yfr+vZFzl/4nBdYcmviBPtwGSX9TJ7wpVg28ocerKVOt+k2eGmHzcgVA==} - engines: {node: ^18.18.0 || >=20.0.0} + typescript-eslint@8.0.0-alpha.14: + resolution: {integrity: sha512-Un2y0pbBCdvmk2YsY/S/oftSA/4tEZtRMfewHlXJ43LBR07V2HSXPC/t6RJ29KZ+N5ORqe61QUQLupquVBPZhQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: @@ -1892,6 +1890,9 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -2306,34 +2307,30 @@ snapshots: '@types/pug@2.0.10': {} - '@types/semver@7.5.8': {} - - '@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@8.0.0-alpha.14(@typescript-eslint/parser@8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.8.0(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/type-utils': 7.8.0(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/utils': 7.8.0(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.8.0 - debug: 4.3.4 + '@typescript-eslint/parser': 8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 8.0.0-alpha.14 + '@typescript-eslint/type-utils': 8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/utils': 8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.0.0-alpha.14 eslint: 9.2.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 - semver: 7.6.1 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.8.0(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/parser@8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5)': dependencies: - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.8.0 + '@typescript-eslint/scope-manager': 8.0.0-alpha.14 + '@typescript-eslint/types': 8.0.0-alpha.14 + '@typescript-eslint/typescript-estree': 8.0.0-alpha.14(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.0.0-alpha.14 debug: 4.3.4 eslint: 9.2.0 optionalDependencies: @@ -2341,29 +2338,29 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.8.0': + '@typescript-eslint/scope-manager@8.0.0-alpha.14': dependencies: - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/visitor-keys': 7.8.0 + '@typescript-eslint/types': 8.0.0-alpha.14 + '@typescript-eslint/visitor-keys': 8.0.0-alpha.14 - '@typescript-eslint/type-utils@7.8.0(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/type-utils@8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5)': dependencies: - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) - '@typescript-eslint/utils': 7.8.0(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/typescript-estree': 8.0.0-alpha.14(typescript@5.4.5) + '@typescript-eslint/utils': 8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5) debug: 4.3.4 - eslint: 9.2.0 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: + - eslint - supports-color - '@typescript-eslint/types@7.8.0': {} + '@typescript-eslint/types@8.0.0-alpha.14': {} - '@typescript-eslint/typescript-estree@7.8.0(typescript@5.4.5)': + '@typescript-eslint/typescript-estree@8.0.0-alpha.14(typescript@5.4.5)': dependencies: - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/visitor-keys': 7.8.0 + '@typescript-eslint/types': 8.0.0-alpha.14 + '@typescript-eslint/visitor-keys': 8.0.0-alpha.14 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -2375,23 +2372,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.8.0(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/utils@8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) + '@typescript-eslint/scope-manager': 8.0.0-alpha.14 + '@typescript-eslint/types': 8.0.0-alpha.14 + '@typescript-eslint/typescript-estree': 8.0.0-alpha.14(typescript@5.4.5) eslint: 9.2.0 - semver: 7.6.1 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@7.8.0': + '@typescript-eslint/visitor-keys@8.0.0-alpha.14': dependencies: - '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/types': 8.0.0-alpha.14 eslint-visitor-keys: 3.4.3 acorn-jsx@5.3.2(acorn@8.11.3): @@ -3463,15 +3457,15 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typescript-eslint@7.8.0(eslint@9.2.0)(typescript@5.4.5): + typescript-eslint@8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5): dependencies: - '@typescript-eslint/eslint-plugin': 7.8.0(@typescript-eslint/parser@7.8.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/parser': 7.8.0(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/utils': 7.8.0(eslint@9.2.0)(typescript@5.4.5) - eslint: 9.2.0 + '@typescript-eslint/eslint-plugin': 8.0.0-alpha.14(@typescript-eslint/parser@8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/parser': 8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/utils': 8.0.0-alpha.14(eslint@9.2.0)(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: + - eslint - supports-color typescript@5.4.5: {} @@ -3541,3 +3535,5 @@ snapshots: yaml@2.4.2: {} yocto-queue@0.1.0: {} + + zod@3.23.8: {} diff --git a/Website/postcss.config.cjs b/Website/postcss.config.cjs index fe10e55a8..3a310c7e3 100644 --- a/Website/postcss.config.cjs +++ b/Website/postcss.config.cjs @@ -2,12 +2,12 @@ const tailwindcss = require('tailwindcss'); const autoprefixer = require('autoprefixer'); const config = { - plugins: [ - //Some plugins, like tailwindcss/nesting, need to run before Tailwind, - tailwindcss(), - //But others, like autoprefixer, need to run after, - autoprefixer - ] + plugins: [ + //Some plugins, like tailwindcss/nesting, need to run before Tailwind, + tailwindcss(), + //But others, like autoprefixer, need to run after, + autoprefixer + ] }; module.exports = config; diff --git a/Website/src/app.d.ts b/Website/src/app.d.ts index 743f07b2e..ede601ab9 100644 --- a/Website/src/app.d.ts +++ b/Website/src/app.d.ts @@ -1,13 +1,13 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } } export {}; diff --git a/Website/src/app.html b/Website/src/app.html index ba7c9dbc9..9a27b47d2 100644 --- a/Website/src/app.html +++ b/Website/src/app.html @@ -1,13 +1,13 @@ - - - - - Dawnshard - %sveltekit.head% - - -
%sveltekit.body%
- + + + + + Dawnshard + %sveltekit.head% + + +
%sveltekit.body%
+ diff --git a/Website/src/app.pcss b/Website/src/app.pcss index 7974bb61c..a205c1b6b 100644 --- a/Website/src/app.pcss +++ b/Website/src/app.pcss @@ -3,64 +3,64 @@ @tailwind utilities; @layer base { - :root { - --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; - --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; - --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - --primary: 221.2 83.2% 53.3%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 72.22% 50.59%; - --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 221.2 83.2% 53.3%; - --radius: 0.75rem; - --divider: rgba(99 107 116 / 0.2); - } - .dark { - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; - --primary: 217.2 91.2% 59.8%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 224.3 76.3% 48%; - --divider: rgba(159 166 173 / 0.16); - } + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 221.2 83.2% 53.3%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 72.22% 50.59%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 221.2 83.2% 53.3%; + --radius: 0.75rem; + --divider: rgba(99 107 116 / 0.2); + } + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 217.2 91.2% 59.8%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 224.3 76.3% 48%; + --divider: rgba(159 166 173 / 0.16); + } } @layer base { - * { - @apply border-border; - --header-height: 3.5rem; - --navigation-width: min(25%, 300px); - } - body { - @apply bg-background text-foreground; - } - .link { - @apply text-sky-400; - @apply underline; - } + * { + @apply border-border; + --header-height: 3.5rem; + --navigation-width: min(25%, 300px); + } + body { + @apply bg-background text-foreground; + } + .link { + @apply text-sky-400; + @apply underline; + } } diff --git a/Website/src/hooks.server.ts b/Website/src/hooks.server.ts new file mode 100644 index 000000000..0ee452afe --- /dev/null +++ b/Website/src/hooks.server.ts @@ -0,0 +1,18 @@ +import { PUBLIC_DAWNSHARD_API_URL } from '$env/static/public'; +import { DAWNSHARD_API_URL_SSR } from '$env/static/private'; + +const publicApiUrl = new URL(PUBLIC_DAWNSHARD_API_URL); +const internalApiUrl = new URL(DAWNSHARD_API_URL_SSR); + +export const handleFetch = async ({ request, fetch }) => { + const requestUrl = new URL(request.url); + if (requestUrl.origin === publicApiUrl.origin) { + // Rewrite URL to internal + const newUrl = request.url.replace(publicApiUrl.origin, internalApiUrl.origin); + console.log('Rewrote URL', requestUrl.toString(), 'to', newUrl.toString()); + + return await fetch(new Request(newUrl, request)); + } + + return await fetch(request); +}; diff --git a/Website/src/lib/components/routes.svelte b/Website/src/lib/components/routes.svelte index a8742a43d..2a7f8e454 100644 --- a/Website/src/lib/components/routes.svelte +++ b/Website/src/lib/components/routes.svelte @@ -1,15 +1,15 @@ {#each routeGroups as routeGroup} -

{routeGroup.title}

- {#each routeGroup.routes as route} - - {/each} +

{routeGroup.title}

+ {#each routeGroup.routes as route} + + {/each} {/each} diff --git a/Website/src/lib/components/typography.svelte b/Website/src/lib/components/typography.svelte index 22a568113..f76f31a18 100644 --- a/Website/src/lib/components/typography.svelte +++ b/Website/src/lib/components/typography.svelte @@ -1,18 +1,18 @@ {#if typography === 'h1'} -

+

{:else if typography === 'h2'} -

+

{:else if typography === 'h3'} -

+

{:else if typography === 'h4'} -

+

{/if} diff --git a/Website/src/lib/cookies.ts b/Website/src/lib/cookies.ts new file mode 100644 index 000000000..c7768e525 --- /dev/null +++ b/Website/src/lib/cookies.ts @@ -0,0 +1,6 @@ +enum Cookies { + ChallengeString = 'challengeString', + IdToken = 'idToken' +} + +export default Cookies; diff --git a/Website/src/lib/jwt.ts b/Website/src/lib/jwt.ts new file mode 100644 index 000000000..a7106eb28 --- /dev/null +++ b/Website/src/lib/jwt.ts @@ -0,0 +1,44 @@ +export type JwtMetadata = + | { valid: false } + | { + valid: true; + expiryTimestampMs: number; + }; + +const getJwtMetadata = (jwt: string): JwtMetadata => { + const segments = jwt.split('.'); + if (segments.length < 3) { + return { valid: false }; + } + + const payload = segments[1]; + let payloadObject; + + try { + const decodedPayload = atob(payload); + payloadObject = JSON.parse(decodedPayload); + } catch { + return { valid: false }; + } + + // We only care here if the token is expired. We are not validating signatures, etc. because that + // can be left to the main API server. This is used to inform non-security-critical things like + // whether to show the login button, or how long to store the JWT in a cookie for. + const exp = payloadObject.exp; + if (!exp || !Number.isInteger(exp)) { + return { valid: false }; + } + + const expDate = new Date(exp * 1000); + if (!Number.isInteger(expDate.valueOf())) { + return { valid: false }; + } + + return { + valid: true, + expiry: expDate, + expiryTimestampMs: exp * 1000 + }; +}; + +export default getJwtMetadata; diff --git a/Website/src/lib/routes.ts b/Website/src/lib/routes.ts index c1fd4b2be..b641f3022 100644 --- a/Website/src/lib/routes.ts +++ b/Website/src/lib/routes.ts @@ -3,22 +3,22 @@ import type { ComponentType } from 'svelte'; import * as Icons from './icons'; export type RouteGroup = { - title: string; - routes: Route[]; + title: string; + routes: Route[]; }; export type Route = { - title: string; - href: string; - icon: ComponentType; + title: string; + href: string; + icon: ComponentType; }; export const routeGroups: RouteGroup[] = [ - { - title: 'Information', - routes: [ - { title: 'Home', href: '/', icon: Icons.Home }, - { title: 'News', href: '/news', icon: Icons.Newspaper } - ] - } + { + title: 'Information', + routes: [ + { title: 'Home', href: '/', icon: Icons.Home }, + { title: 'News', href: '/news', icon: Icons.Newspaper } + ] + } ]; diff --git a/Website/src/routes/(main)/(home)/+page.svelte b/Website/src/routes/(main)/(home)/+page.svelte index 618346eed..52126dce0 100644 --- a/Website/src/routes/(main)/(home)/+page.svelte +++ b/Website/src/routes/(main)/(home)/+page.svelte @@ -1,51 +1,51 @@
@@ -58,8 +58,8 @@ possible to progress largely as normal.

-
-
+
+
How to play

Setting up access to the server involves configuring a modified version of the original @@ -72,21 +72,8 @@ community Discord server.

-
+
Android -
  1. Ensure you have the original Dragalia Lost app installed. The Dragalipatch app works by @@ -112,6 +99,21 @@ on the screen that follows.
+
+ +
iOS

For information on how to play on iOS, please see{' '} @@ -214,17 +216,17 @@

diff --git a/Website/src/routes/(main)/(home)/acknowledgement.svelte b/Website/src/routes/(main)/(home)/acknowledgement.svelte index fc9128af6..672a246f8 100644 --- a/Website/src/routes/(main)/(home)/acknowledgement.svelte +++ b/Website/src/routes/(main)/(home)/acknowledgement.svelte @@ -1,13 +1,13 @@
  • - - - -

    {name},

    + + + +

    {name},

  • diff --git a/Website/src/routes/(main)/(home)/icons/buyMeACoffee.svelte b/Website/src/routes/(main)/(home)/icons/buyMeACoffee.svelte index 82c15c645..70edf70a9 100644 --- a/Website/src/routes/(main)/(home)/icons/buyMeACoffee.svelte +++ b/Website/src/routes/(main)/(home)/icons/buyMeACoffee.svelte @@ -1,5 +1,5 @@ Buy Me A CoffeeBuy Me A Coffee diff --git a/Website/src/routes/(main)/(home)/icons/discord.svelte b/Website/src/routes/(main)/(home)/icons/discord.svelte index 3e032833a..d627c3ace 100644 --- a/Website/src/routes/(main)/(home)/icons/discord.svelte +++ b/Website/src/routes/(main)/(home)/icons/discord.svelte @@ -1,5 +1,5 @@ diff --git a/Website/src/routes/(main)/(home)/icons/github.svelte b/Website/src/routes/(main)/(home)/icons/github.svelte index 825e5815a..6434fac4d 100644 --- a/Website/src/routes/(main)/(home)/icons/github.svelte +++ b/Website/src/routes/(main)/(home)/icons/github.svelte @@ -1,7 +1,7 @@ diff --git a/Website/src/routes/(main)/(home)/icons/patreon.svelte b/Website/src/routes/(main)/(home)/icons/patreon.svelte index 6431c9fae..343672f5b 100644 --- a/Website/src/routes/(main)/(home)/icons/patreon.svelte +++ b/Website/src/routes/(main)/(home)/icons/patreon.svelte @@ -1,19 +1,19 @@ - + /> diff --git a/Website/src/routes/(main)/(home)/linkButton.svelte b/Website/src/routes/(main)/(home)/linkButton.svelte index 982b30fa3..03a876953 100644 --- a/Website/src/routes/(main)/(home)/linkButton.svelte +++ b/Website/src/routes/(main)/(home)/linkButton.svelte @@ -1,14 +1,14 @@ diff --git a/Website/src/routes/(main)/+layout.server.ts b/Website/src/routes/(main)/+layout.server.ts new file mode 100644 index 000000000..e75355a5a --- /dev/null +++ b/Website/src/routes/(main)/+layout.server.ts @@ -0,0 +1,25 @@ +import type { LayoutServerLoad } from './$types'; +import getJwtMetadata from '$lib/jwt'; +import Cookies from '$lib/cookies'; + +export const load: LayoutServerLoad = ({ cookies, depends }) => { + depends(`cookie:${Cookies.IdToken}`); + + const idToken = cookies.get(Cookies.IdToken); + if (!idToken) { + return { + hasValidJwt: false + }; + } + + const jwtMetadata = getJwtMetadata(idToken); + if (!jwtMetadata.valid || Date.now() > jwtMetadata.expiryTimestampMs) { + return { + hasValidJwt: false + }; + } + + return { + hasValidJwt: true + }; +}; diff --git a/Website/src/routes/(main)/+layout.svelte b/Website/src/routes/(main)/+layout.svelte index 0f1158f98..a745dfd41 100644 --- a/Website/src/routes/(main)/+layout.svelte +++ b/Website/src/routes/(main)/+layout.svelte @@ -1,14 +1,17 @@ -
    +
    - +
    diff --git a/Website/src/routes/(main)/header.svelte b/Website/src/routes/(main)/header.svelte index 9584255e1..b33ab9ea0 100644 --- a/Website/src/routes/(main)/header.svelte +++ b/Website/src/routes/(main)/header.svelte @@ -1,84 +1,71 @@ {#if enhance} - - + {:else} - + {/if} diff --git a/Website/src/routes/(main)/headerContents.svelte b/Website/src/routes/(main)/headerContents.svelte new file mode 100644 index 000000000..a6334b3ff --- /dev/null +++ b/Website/src/routes/(main)/headerContents.svelte @@ -0,0 +1,34 @@ + + +

    Dawnshard

    +
    + + +{#if hasValidJwt} + +{:else} + +{/if} diff --git a/Website/src/routes/(main)/login/+page.server.ts b/Website/src/routes/(main)/login/+page.server.ts new file mode 100644 index 000000000..fee8b27c6 --- /dev/null +++ b/Website/src/routes/(main)/login/+page.server.ts @@ -0,0 +1,40 @@ +import { redirect } from '@sveltejs/kit'; +import { Buffer } from 'buffer'; +import type { PageServerLoad } from './$types'; +import { PUBLIC_BAAS_URL, PUBLIC_BAAS_CLIENT_ID, PUBLIC_DAWNSHARD_URL } from '$env/static/public'; + +const redirectUri = new URL('oauth', PUBLIC_DAWNSHARD_URL); + +const getChallengeString = () => { + const buffer = new Uint8Array(8); + crypto.getRandomValues(buffer); + return Array.from(buffer, (dec) => dec.toString(16).padStart(2, '0')).join(''); +}; + +const getUrlSafeBase64Hash = async (input: string) => { + const buffer = new TextEncoder().encode(input); + const hashBuffer = await crypto.subtle.digest('SHA-256', buffer); + const base64 = Buffer.from(new Uint8Array(hashBuffer)).toString('base64'); + return base64.replace('+', '-').replace('/', '_').replace('=', ''); +}; + +export const load: PageServerLoad = async ({ cookies, url }) => { + const originalPage = url.searchParams.get('originalPage') ?? '/'; + + const challengeStringValue = getChallengeString(); + cookies.set('challengeString', challengeStringValue, { path: '/' }); + + const queryParams = new URLSearchParams({ + client_id: PUBLIC_BAAS_CLIENT_ID, + redirect_uri: redirectUri.toString(), + response_type: 'session_token_code', + scope: 'user user.birthday openid', + language: 'en-US', + session_token_code_challenge: await getUrlSafeBase64Hash(challengeStringValue), + session_token_code_challenge_method: 'S256', + state: JSON.stringify({ originalPage }) + }); + + const baasUrl = new URL(`/custom/thirdparty/auth?${queryParams}`, PUBLIC_BAAS_URL); + redirect(302, baasUrl); +}; diff --git a/Website/src/routes/(main)/logout/+page.server.ts b/Website/src/routes/(main)/logout/+page.server.ts new file mode 100644 index 000000000..5dec19bc0 --- /dev/null +++ b/Website/src/routes/(main)/logout/+page.server.ts @@ -0,0 +1,9 @@ +import type { PageServerLoad } from './$types'; +import Cookies from '$lib/cookies'; +import { redirect } from '@sveltejs/kit'; + +export const load: PageServerLoad = async ({ cookies }) => { + cookies.delete(Cookies.IdToken, { path: '/' }); + + redirect(302, '/'); +}; diff --git a/Website/src/routes/(main)/navigation/+page@.svelte b/Website/src/routes/(main)/navigation/+page@.svelte index 7039a19d0..e12d57af1 100644 --- a/Website/src/routes/(main)/navigation/+page@.svelte +++ b/Website/src/routes/(main)/navigation/+page@.svelte @@ -1,8 +1,8 @@ diff --git a/Website/src/routes/(main)/news/+page.svelte b/Website/src/routes/(main)/news/+page.svelte new file mode 100644 index 000000000..e69de29bb diff --git a/Website/src/routes/(main)/oauth/+page.server.ts b/Website/src/routes/(main)/oauth/+page.server.ts new file mode 100644 index 000000000..97fd74f89 --- /dev/null +++ b/Website/src/routes/(main)/oauth/+page.server.ts @@ -0,0 +1,115 @@ +import type { PageServerLoad } from './$types'; +import { PUBLIC_BAAS_URL, PUBLIC_BAAS_CLIENT_ID } from '$env/static/public'; +import Cookies from '$lib/cookies'; +import { redirect } from '@sveltejs/kit'; +import getJwtMetadata from '$lib/jwt'; + +const sessionTokenUrl = new URL('/connect/1.0.0/api/session_token', PUBLIC_BAAS_URL); +const sdkTokenUrl = new URL('/1.0.0/gateway/sdk/token', PUBLIC_BAAS_URL); + +const getOriginalPage = (url: URL) => { + const stateJson = url.searchParams.get('state'); + if (!stateJson) { + return null; + } + + let stateObject; + try { + stateObject = JSON.parse(stateJson); + } catch { + return null; + } + + if (!stateObject.originalPage) { + return null; + } + + return stateObject.originalPage; +}; + +export const load: PageServerLoad = async ({ cookies, url, fetch }) => { + const challengeString = cookies.get(Cookies.ChallengeString); + + if (!challengeString) { + throw new Error('Failed to get challenge string'); + } + + const sessionTokenCode = url.searchParams.get('session_token_code'); + + if (!sessionTokenCode) { + throw new Error('Failed to get session token code'); + } + + const sessionTokenCodeParams = new URLSearchParams({ + client_id: PUBLIC_BAAS_CLIENT_ID, + session_token_code: sessionTokenCode, + session_token_code_verifier: challengeString + }); + + const sessionTokenResponse = await fetch(sessionTokenUrl, { + method: 'POST', + body: sessionTokenCodeParams + }); + + if (!sessionTokenResponse.ok) { + throw new Error('Session token request failed'); + } + + const sessionTokenResponseBody = await sessionTokenResponse.json(); + const sessionToken = sessionTokenResponseBody.session_token; + + if (!sessionToken) { + throw new Error('Failed to parse session token response'); + } + + const sdkTokenRequest = { + client_id: PUBLIC_BAAS_CLIENT_ID, + session_token: sessionToken + }; + + const sdkTokenResponse = await fetch(sdkTokenUrl, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(sdkTokenRequest) + }); + + if (!sdkTokenResponse.ok) { + throw new Error('SDK token request failed'); + } + + const sdkTokenResponseBody = await sdkTokenResponse.json(); + const idToken = sdkTokenResponseBody.idToken; + + if (!idToken) { + throw new Error('Failed to parse SDK token response'); + } + + const jwtMetadata = getJwtMetadata(idToken); + if (!jwtMetadata.valid) { + throw Error('Invalid JWT returned'); + } + + console.log(jwtMetadata); + + const maxAge = (jwtMetadata.expiryTimestampMs - Date.now()) / 1000; + + cookies.set(Cookies.IdToken, idToken, { + path: '/', + sameSite: 'lax', + httpOnly: true, + maxAge, + ...(import.meta.env.MODE !== 'development' && { + secure: true + }) + }); + + cookies.delete('challengeString', { + path: '/' + }); + + const destination = getOriginalPage(url) ?? '/'; + redirect(302, destination); +}; diff --git a/Website/src/routes/(main)/sideNav.svelte b/Website/src/routes/(main)/sideNav.svelte index 7f4417979..9bdd8ae28 100644 --- a/Website/src/routes/(main)/sideNav.svelte +++ b/Website/src/routes/(main)/sideNav.svelte @@ -1,21 +1,21 @@ diff --git a/Website/src/routes/(main)/user.ts b/Website/src/routes/(main)/user.ts new file mode 100644 index 000000000..17911602a --- /dev/null +++ b/Website/src/routes/(main)/user.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; + +export const userSchema = z.object({ + viewerId: z.number().int(), + name: z.string() +}); + +export type User = z.infer; diff --git a/Website/src/routes/+layout.svelte b/Website/src/routes/+layout.svelte index 17abf18a5..e163a965b 100644 --- a/Website/src/routes/+layout.svelte +++ b/Website/src/routes/+layout.svelte @@ -1,5 +1,5 @@ diff --git a/Website/svelte.config.js b/Website/svelte.config.js index c9d691feb..4d0172766 100644 --- a/Website/svelte.config.js +++ b/Website/svelte.config.js @@ -3,20 +3,20 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { - // Consult https://kit.svelte.dev/docs/integrations#preprocessors - // for more information about preprocessors - preprocess: [vitePreprocess({})], + // Consult https://kit.svelte.dev/docs/integrations#preprocessors + // for more information about preprocessors + preprocess: [vitePreprocess({})], - kit: { - // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. - // If your environment is not supported or you settled on a specific environment, switch out the adapter. - // See https://kit.svelte.dev/docs/adapters for more information about adapters. - adapter: adapter(), - alias: { - $shadcn: './src/lib/shadcn', - $static: './static' - } - } + kit: { + // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. + // If your environment is not supported or you settled on a specific environment, switch out the adapter. + // See https://kit.svelte.dev/docs/adapters for more information about adapters. + adapter: adapter(), + alias: { + $shadcn: './src/lib/shadcn', + $static: './static' + } + } }; export default config; diff --git a/Website/tailwind.config.js b/Website/tailwind.config.js index 3ff3115e9..ea97c4816 100644 --- a/Website/tailwind.config.js +++ b/Website/tailwind.config.js @@ -2,68 +2,68 @@ import { fontFamily } from 'tailwindcss/defaultTheme'; /** @type {import('tailwindcss').Config} */ const config = { - darkMode: 'selector', - content: ['./src/**/*.{html,js,svelte,ts}'], - safelist: ['dark'], - theme: { - container: { - center: true, - padding: '2rem', - screens: { - '2xl': '1400px' - } - }, - extend: { - colors: { - border: 'hsl(var(--border) / )', - input: 'hsl(var(--input) / )', - ring: 'hsl(var(--ring) / )', - background: 'hsl(var(--background) / )', - foreground: 'hsl(var(--foreground) / )', - primary: { - DEFAULT: 'hsl(var(--primary) / )', - foreground: 'hsl(var(--primary-foreground) / )' - }, - secondary: { - DEFAULT: 'hsl(var(--secondary) / )', - foreground: 'hsl(var(--secondary-foreground) / )' - }, - destructive: { - DEFAULT: 'hsl(var(--destructive) / )', - foreground: 'hsl(var(--destructive-foreground) / )' - }, - muted: { - DEFAULT: 'hsl(var(--muted) / )', - foreground: 'hsl(var(--muted-foreground) / )' - }, - accent: { - DEFAULT: 'hsl(var(--accent) / )', - foreground: 'hsl(var(--accent-foreground) / )' - }, - popover: { - DEFAULT: 'hsl(var(--popover) / )', - foreground: 'hsl(var(--popover-foreground) / )' - }, - card: { - DEFAULT: 'hsl(var(--card) / )', - foreground: 'hsl(var(--card-foreground) / )' - } - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - }, - fontFamily: { - sans: [...fontFamily.sans] - } - } - }, - variants: { - extend: { - backgroundImage: ['dark'] - } - } + darkMode: 'selector', + content: ['./src/**/*.{html,js,svelte,ts}'], + safelist: ['dark'], + theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px' + } + }, + extend: { + colors: { + border: 'hsl(var(--border) / )', + input: 'hsl(var(--input) / )', + ring: 'hsl(var(--ring) / )', + background: 'hsl(var(--background) / )', + foreground: 'hsl(var(--foreground) / )', + primary: { + DEFAULT: 'hsl(var(--primary) / )', + foreground: 'hsl(var(--primary-foreground) / )' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary) / )', + foreground: 'hsl(var(--secondary-foreground) / )' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive) / )', + foreground: 'hsl(var(--destructive-foreground) / )' + }, + muted: { + DEFAULT: 'hsl(var(--muted) / )', + foreground: 'hsl(var(--muted-foreground) / )' + }, + accent: { + DEFAULT: 'hsl(var(--accent) / )', + foreground: 'hsl(var(--accent-foreground) / )' + }, + popover: { + DEFAULT: 'hsl(var(--popover) / )', + foreground: 'hsl(var(--popover-foreground) / )' + }, + card: { + DEFAULT: 'hsl(var(--card) / )', + foreground: 'hsl(var(--card-foreground) / )' + } + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + fontFamily: { + sans: [...fontFamily.sans] + } + } + }, + variants: { + extend: { + backgroundImage: ['dark'] + } + } }; export default config; diff --git a/Website/tests/test.ts b/Website/tests/test.ts index 5816be413..589fab7b0 100644 --- a/Website/tests/test.ts +++ b/Website/tests/test.ts @@ -1,6 +1,6 @@ import { expect, test } from '@playwright/test'; test('index page has expected h1', async ({ page }) => { - await page.goto('/'); - await expect(page.getByRole('heading', { name: 'Welcome to SvelteKit' })).toBeVisible(); + await page.goto('/'); + await expect(page.getByRole('heading', { name: 'Welcome to SvelteKit' })).toBeVisible(); }); diff --git a/Website/tsconfig.json b/Website/tsconfig.json index 82081abc3..34aadc028 100644 --- a/Website/tsconfig.json +++ b/Website/tsconfig.json @@ -1,18 +1,18 @@ { - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "bundler" - } - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in } diff --git a/Website/vite.config.ts b/Website/vite.config.ts index c098bb5b0..64b9db012 100644 --- a/Website/vite.config.ts +++ b/Website/vite.config.ts @@ -3,8 +3,14 @@ import { enhancedImages } from '@sveltejs/enhanced-img'; import { defineConfig } from 'vite'; export default defineConfig({ - plugins: [sveltekit(), enhancedImages()], - server: { - port: 3001 - } + plugins: [sveltekit(), enhancedImages()], + server: { + port: 3001, + proxy: { + '/api': { + target: 'http://localhost:5000', + changeOrigin: true + } + } + } });