Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8bdd9edbb0 | |||
| 5e6b0b21a6 | |||
| 19274d444d | |||
| 63e2f9f791 | |||
| 8964d1c069 | |||
| 8808126905 | |||
| d36aaac836 | |||
| bb2c6c4683 | |||
| f8d457a096 |
46
.github/workflows/.woodpecker.yaml
vendored
46
.github/workflows/.woodpecker.yaml
vendored
@@ -1,46 +0,0 @@
|
|||||||
pipeline:
|
|
||||||
build-and-push:
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
dockerfile: AobaServer/Dockerfile
|
|
||||||
context: .
|
|
||||||
repo: git.kaisei.app/amatsugu/aoba
|
|
||||||
tags: latest
|
|
||||||
username:
|
|
||||||
from_secret: docker_user
|
|
||||||
password:
|
|
||||||
from_secret: docker_pass
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
image: appleboy/ssh
|
|
||||||
settings:
|
|
||||||
host: your-app-host.internal
|
|
||||||
username: deploy
|
|
||||||
key:
|
|
||||||
from_secret: ssh_key
|
|
||||||
port: 22
|
|
||||||
script:
|
|
||||||
- docker pull git.kaisei.app/amatsugu/aoba:latest
|
|
||||||
|
|
||||||
# Run temporary container on docker network, no port binding
|
|
||||||
- docker run -d --rm \
|
|
||||||
--name aoba-temp \
|
|
||||||
--network aoba-net \
|
|
||||||
git.kaisei.app/amatsugu/aoba:latest
|
|
||||||
|
|
||||||
# Wait for it to become healthy
|
|
||||||
- sleep 3
|
|
||||||
- curl -f http://aoba-temp:8080 --connect-timeout 2 || (echo "Health check failed" && docker stop aoba-temp && exit 1)
|
|
||||||
|
|
||||||
# Stop old container (bound to host port)
|
|
||||||
- docker stop aoba || true
|
|
||||||
|
|
||||||
# Start new container on network and bind to host port 8080
|
|
||||||
- docker run -d --rm \
|
|
||||||
--name aoba \
|
|
||||||
--network aoba-net \
|
|
||||||
-p 9432:8080 \
|
|
||||||
git.kaisei.app/amatsugu/aoba:latest
|
|
||||||
|
|
||||||
# Stop temp container
|
|
||||||
- docker stop aoba-temp || true
|
|
||||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkou code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Docker Build
|
- name: Set up Docker Build
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ public class AobaService(IMongoDatabase db)
|
|||||||
return new PagedResult<Media>(items, page, pageSize, total);
|
return new PagedResult<Media>(items, page, pageSize, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<Media>> FindMediaWithExtAsync(string ext, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var filter = Builders<Media>.Filter.Eq(m => m.Ext, ext);
|
||||||
|
return await _media.Find(filter).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public Task AddMediaAsync(Media media, CancellationToken cancellationToken = default)
|
public Task AddMediaAsync(Media media, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
@@ -53,6 +58,13 @@ public class AobaService(IMongoDatabase db)
|
|||||||
await _media.UpdateOneAsync(m => m.MediaId == mediaId, upate, cancellationToken: cancellationToken);
|
await _media.UpdateOneAsync(m => m.MediaId == mediaId, upate, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task RemoveThumbnailAsync(ObjectId mediaId, ThumbnailSize size, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var upate = Builders<Media>.Update.Unset(m => m.Thumbnails[size]);
|
||||||
|
|
||||||
|
await _media.UpdateOneAsync(m => m.MediaId == mediaId, upate, cancellationToken: cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ObjectId> GetThumbnailIdAsync(ObjectId mediaId, ThumbnailSize size, CancellationToken cancellationToken = default)
|
public async Task<ObjectId> GetThumbnailIdAsync(ObjectId mediaId, ThumbnailSize size, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var thumb = await _media.Find(m => m.MediaId == mediaId).Project(m => m.Thumbnails[size]).FirstOrDefaultAsync(cancellationToken);
|
var thumb = await _media.Find(m => m.MediaId == mediaId).Project(m => m.Thumbnails[size]).FirstOrDefaultAsync(cancellationToken);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using SixLabors.ImageSharp.Processing;
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -27,6 +28,28 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
|
|||||||
{
|
{
|
||||||
private readonly GridFSBucket _gridfs = new GridFSBucket(db);
|
private readonly GridFSBucket _gridfs = new GridFSBucket(db);
|
||||||
|
|
||||||
|
public async Task<Error?> DeleteThumbnailAsync(ObjectId mediaId, ThumbnailSize size)
|
||||||
|
{
|
||||||
|
var thumbId = await aobaService.GetThumbnailIdAsync(mediaId, size);
|
||||||
|
if (thumbId == default)
|
||||||
|
return null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _gridfs.DeleteAsync(thumbId);
|
||||||
|
await aobaService.RemoveThumbnailAsync(mediaId, size);
|
||||||
|
}
|
||||||
|
catch (GridFSFileNotFoundException)
|
||||||
|
{
|
||||||
|
//Ignore if the file was not found (somehow already deleted)
|
||||||
|
await aobaService.RemoveThumbnailAsync(mediaId, size);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return new ExceptionError(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -36,6 +59,7 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<Maybe<Stream>> GetOrCreateThumbnailAsync(ObjectId mediaId, ThumbnailSize size, CancellationToken cancellationToken = default)
|
public async Task<Maybe<Stream>> GetOrCreateThumbnailAsync(ObjectId mediaId, ThumbnailSize size, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
|
||||||
var existingThumb = await GetThumbnailAsync(mediaId, size, cancellationToken);
|
var existingThumb = await GetThumbnailAsync(mediaId, size, cancellationToken);
|
||||||
if (existingThumb != null)
|
if (existingThumb != null)
|
||||||
return existingThumb;
|
return existingThumb;
|
||||||
@@ -132,7 +156,7 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Maybe<Stream> GenerateVideoThumbnail(Stream data, ThumbnailSize size, CancellationToken cancellationToken = default)
|
public static Maybe<Stream> GenerateVideoThumbnail(Stream data, ThumbnailSize size, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var w = (int)size;
|
var w = (int)size;
|
||||||
var fn = ObjectId.GenerateNewId().ToString();
|
var fn = ObjectId.GenerateNewId().ToString();
|
||||||
@@ -166,6 +190,47 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Maybe<Stream> GenerateAvifThumbnail(Stream data, ThumbnailSize size, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var w = (int)size;
|
||||||
|
var fn = ObjectId.GenerateNewId().ToString();
|
||||||
|
var inFilePath = $"/tmp/{fn}.avif";
|
||||||
|
var outFilePath = $"/tmp/{fn}.webp";
|
||||||
|
using var source = new FileStream(inFilePath, FileMode.CreateNew);
|
||||||
|
data.CopyTo(source);
|
||||||
|
source.Flush();
|
||||||
|
source.Dispose();
|
||||||
|
data.Dispose();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var process = Process.Start(new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "vips",
|
||||||
|
Arguments = $"smartcrop \"{inFilePath}\" \"{outFilePath}\"[Q=75] {w} {w}",
|
||||||
|
WorkingDirectory = "/tmp"
|
||||||
|
});
|
||||||
|
if (process == null)
|
||||||
|
return new Error("Failed to run vips command");
|
||||||
|
process.WaitForExit();
|
||||||
|
if (process.ExitCode != 0)
|
||||||
|
return new Error("Failed to convert");
|
||||||
|
var output = new MemoryStream();
|
||||||
|
using var oFile = File.OpenRead(outFilePath);
|
||||||
|
oFile.CopyTo(output);
|
||||||
|
output.Position = 0;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
File.Delete(inFilePath);
|
||||||
|
File.Delete(outFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Maybe<Stream>> GenerateDocumentThumbnailAsync(Stream data, ThumbnailSize size, CancellationToken cancellationToken = default)
|
public async Task<Maybe<Stream>> GenerateDocumentThumbnailAsync(Stream data, ThumbnailSize size, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return new NotImplementedException();
|
return new NotImplementedException();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace AobaServer.Controllers;
|
|||||||
public class MediaController(AobaService aobaService, ILogger<MediaController> logger) : Controller
|
public class MediaController(AobaService aobaService, ILogger<MediaController> logger) : Controller
|
||||||
{
|
{
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
[HttpGet("{id}/*")]
|
[HttpGet("{id}/{*fn}")]
|
||||||
[ResponseCache(Duration = int.MaxValue)]
|
[ResponseCache(Duration = int.MaxValue)]
|
||||||
public async Task<IActionResult> MediaAsync(ObjectId id, [FromServices] MongoClient client, CancellationToken cancellationToken)
|
public async Task<IActionResult> MediaAsync(ObjectId id, [FromServices] MongoClient client, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -29,6 +29,22 @@ public class MediaController(AobaService aobaService, ILogger<MediaController> l
|
|||||||
return File(file, mime, true);
|
return File(file, mime, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}/dl")]
|
||||||
|
[HttpGet("{id}/dl/{*fn}")]
|
||||||
|
[ResponseCache(Duration = int.MaxValue)]
|
||||||
|
public async Task<IActionResult> DownloadMediaAsync(ObjectId id, [FromServices] MongoClient client, [FromQuery] string? fn = null, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var file = await aobaService.GetFileStreamAsync(id, seekable: true, cancellationToken: cancellationToken);
|
||||||
|
if (file.HasError)
|
||||||
|
{
|
||||||
|
logger.LogError(file.Error.Exception, "Failed to load media stream");
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
var mime = MimeTypesMap.GetMimeType(file.Value.FileInfo.Filename);
|
||||||
|
_ = aobaService.IncrementViewCountAsync(id, cancellationToken);
|
||||||
|
return File(file, mime, fn ?? file.Value.FileInfo.Filename, true);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Redirect legacy media urls to the new url
|
/// Redirect legacy media urls to the new url
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ RUN apt install -y protobuf-compiler libprotobuf-dev ffmpeg
|
|||||||
|
|
||||||
# Install `dx`
|
# Install `dx`
|
||||||
RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
||||||
RUN cargo binstall dioxus-cli --root /.cargo -y --force
|
RUN cargo binstall dioxus-cli@0.6.3 --root /.cargo -y --force
|
||||||
ENV PATH="/.cargo/bin:$PATH"
|
ENV PATH="/.cargo/bin:$PATH"
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
ENV APP_VERSION=$VERSION
|
ENV APP_VERSION=$VERSION
|
||||||
@@ -32,7 +32,7 @@ RUN dx bundle --platform web
|
|||||||
# Server Build
|
# Server Build
|
||||||
# This stage is used when running from VS in fast mode (Default for Debug configuration)
|
# This stage is used when running from VS in fast mode (Default for Debug configuration)
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||||
RUN apt-get update && apt-get install -y ffmpeg
|
RUN apt-get update && apt-get install -y ffmpeg #libvips libvips-tools
|
||||||
USER $APP_UID
|
USER $APP_UID
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|||||||
@@ -1,164 +1,167 @@
|
|||||||
using AobaCore;
|
using AobaCore;
|
||||||
|
|
||||||
using AobaServer.Auth;
|
using AobaServer.Auth;
|
||||||
using AobaServer.Middleware;
|
using AobaServer.Middleware;
|
||||||
using AobaServer.Models;
|
using AobaServer.Models;
|
||||||
using AobaServer.Services;
|
using AobaServer.Services;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
using MongoDB.Driver.Core.Extensions.DiagnosticSources;
|
using MongoDB.Driver.Core.Extensions.DiagnosticSources;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.WebHost.ConfigureKestrel(o =>
|
builder.WebHost.ConfigureKestrel(o =>
|
||||||
{
|
{
|
||||||
o.Limits.MaxRequestBodySize = null;
|
o.Limits.MaxRequestBodySize = null;
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
o.ListenAnyIP(8081, lo =>
|
o.ListenAnyIP(8081, lo =>
|
||||||
{
|
{
|
||||||
lo.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2;
|
lo.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2;
|
||||||
});
|
});
|
||||||
o.ListenAnyIP(8080, lo =>
|
o.ListenAnyIP(8080, lo =>
|
||||||
{
|
{
|
||||||
lo.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http1AndHttp2;
|
lo.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http1AndHttp2;
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
});
|
});
|
||||||
var config = builder.Configuration;
|
var config = builder.Configuration;
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddControllers(opt => opt.ModelBinderProviders.Add(new BsonIdModelBinderProvider()));
|
builder.Services.AddControllers(opt => opt.ModelBinderProviders.Add(new BsonIdModelBinderProvider()));
|
||||||
|
|
||||||
builder.Services.AddObersability(builder.Configuration);
|
builder.Services.AddObersability(builder.Configuration);
|
||||||
builder.Services.AddGrpc();
|
builder.Services.AddGrpc();
|
||||||
|
|
||||||
//DB
|
//DB
|
||||||
var dbString = config["DB_STRING"];
|
var dbString = config["DB_STRING"];
|
||||||
var settings = MongoClientSettings.FromConnectionString(dbString);
|
var settings = MongoClientSettings.FromConnectionString(dbString);
|
||||||
settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber());
|
settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber());
|
||||||
var dbClient = new MongoClient(settings);
|
var dbClient = new MongoClient(settings);
|
||||||
var db = dbClient.GetDatabase("Aoba");
|
var db = dbClient.GetDatabase("Aoba");
|
||||||
|
|
||||||
builder.Services.AddSingleton(dbClient);
|
builder.Services.AddSingleton(dbClient);
|
||||||
builder.Services.AddSingleton<IMongoDatabase>(db);
|
builder.Services.AddSingleton<IMongoDatabase>(db);
|
||||||
|
|
||||||
var authCfg = new AuthConfigService(db);
|
var authCfg = new AuthConfigService(db);
|
||||||
builder.Services.AddSingleton(authCfg);
|
builder.Services.AddSingleton(authCfg);
|
||||||
|
|
||||||
|
|
||||||
var authInfo = authCfg.GetDefaultAuthInfoAsync().GetAwaiter().GetResult();
|
var authInfo = authCfg.GetDefaultAuthInfoAsync().GetAwaiter().GetResult();
|
||||||
var signingKey = new SymmetricSecurityKey(authInfo.SecureKey);
|
var signingKey = new SymmetricSecurityKey(authInfo.SecureKey);
|
||||||
|
|
||||||
var validationParams = new TokenValidationParameters
|
var validationParams = new TokenValidationParameters
|
||||||
{
|
{
|
||||||
ValidateIssuerSigningKey = true,
|
ValidateIssuerSigningKey = true,
|
||||||
IssuerSigningKey = signingKey,
|
IssuerSigningKey = signingKey,
|
||||||
ValidateIssuer = true,
|
ValidateIssuer = true,
|
||||||
ValidIssuer = authInfo.Issuer,
|
ValidIssuer = authInfo.Issuer,
|
||||||
ValidateAudience = true,
|
ValidateAudience = true,
|
||||||
ValidAudience = authInfo.Audience,
|
ValidAudience = authInfo.Audience,
|
||||||
ValidateLifetime = false,
|
ValidateLifetime = false,
|
||||||
ClockSkew = TimeSpan.FromMinutes(1),
|
ClockSkew = TimeSpan.FromMinutes(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.Services.AddCors(o =>
|
builder.Services.AddCors(o =>
|
||||||
{
|
{
|
||||||
o.AddPolicy("AllowAll", p =>
|
o.AddPolicy("AllowAll", p =>
|
||||||
{
|
{
|
||||||
p.AllowAnyOrigin();
|
p.AllowAnyOrigin();
|
||||||
p.AllowAnyMethod();
|
p.AllowAnyMethod();
|
||||||
p.AllowAnyHeader();
|
p.AllowAnyHeader();
|
||||||
});
|
});
|
||||||
o.AddPolicy("RPC", p =>
|
o.AddPolicy("RPC", p =>
|
||||||
{
|
{
|
||||||
p.AllowAnyMethod();
|
p.AllowAnyMethod();
|
||||||
p.AllowAnyHeader();
|
p.AllowAnyHeader();
|
||||||
p.WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
|
p.WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
|
||||||
p.AllowAnyOrigin();
|
p.AllowAnyOrigin();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var metricsAuthInfo = authCfg.GetAuthInfoAsync("aoba", "metrics").GetAwaiter().GetResult();
|
var metricsAuthInfo = authCfg.GetAuthInfoAsync("aoba", "metrics").GetAwaiter().GetResult();
|
||||||
builder.Services.AddAuthentication(options =>
|
builder.Services.AddAuthentication(options =>
|
||||||
{
|
{
|
||||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
options.DefaultChallengeScheme = "Aoba";
|
options.DefaultChallengeScheme = "Aoba";
|
||||||
}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => //Bearer auth
|
}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => //Bearer auth
|
||||||
{
|
{
|
||||||
options.TokenValidationParameters = validationParams;
|
options.TokenValidationParameters = validationParams;
|
||||||
options.TokenHandlers.Add(new MetricsTokenValidator(metricsAuthInfo));
|
options.TokenHandlers.Add(new MetricsTokenValidator(metricsAuthInfo));
|
||||||
options.Events = new JwtBearerEvents
|
options.Events = new JwtBearerEvents
|
||||||
{
|
{
|
||||||
OnMessageReceived = ctx => //Retreive token from cookie if not found in headers
|
OnMessageReceived = ctx => //Retreive token from cookie if not found in headers
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(ctx.Token))
|
if (string.IsNullOrWhiteSpace(ctx.Token))
|
||||||
ctx.Token = ctx.Request.Headers.Authorization.FirstOrDefault()?.Replace("Bearer ", "");
|
ctx.Token = ctx.Request.Headers.Authorization.FirstOrDefault()?.Replace("Bearer ", "");
|
||||||
|
|
||||||
#if DEBUG //allow cookie based auth when in debug mode
|
#if DEBUG //allow cookie based auth when in debug mode
|
||||||
if (string.IsNullOrWhiteSpace(ctx.Token))
|
if (string.IsNullOrWhiteSpace(ctx.Token))
|
||||||
ctx.Token = ctx.Request.Cookies.FirstOrDefault(c => c.Key == "token").Value;
|
ctx.Token = ctx.Request.Cookies.FirstOrDefault(c => c.Key == "token").Value;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
},
|
},
|
||||||
OnAuthenticationFailed = ctx =>
|
OnAuthenticationFailed = ctx =>
|
||||||
{
|
{
|
||||||
ctx.Response.Cookies.Append("token", "", new CookieOptions
|
ctx.Response.Cookies.Append("token", "", new CookieOptions
|
||||||
{
|
{
|
||||||
MaxAge = TimeSpan.Zero,
|
MaxAge = TimeSpan.Zero,
|
||||||
Expires = DateTime.Now
|
Expires = DateTime.Now
|
||||||
});
|
});
|
||||||
ctx.Options.ForwardChallenge = "Aoba";
|
ctx.Options.ForwardChallenge = "Aoba";
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}).AddScheme<AuthenticationSchemeOptions, AobaAuthenticationHandler>("Aoba", null);
|
}).AddScheme<AuthenticationSchemeOptions, AobaAuthenticationHandler>("Aoba", null);
|
||||||
|
|
||||||
|
|
||||||
builder.Services.AddAoba();
|
builder.Services.AddAoba();
|
||||||
builder.Services.Configure<FormOptions>(opt =>
|
#if DEBUG
|
||||||
{
|
builder.Services.AddHostedService<DebugService>();
|
||||||
opt.ValueLengthLimit = int.MaxValue;
|
#endif
|
||||||
opt.MultipartBodyLengthLimit = int.MaxValue;
|
builder.Services.Configure<FormOptions>(opt =>
|
||||||
});
|
{
|
||||||
|
opt.ValueLengthLimit = int.MaxValue;
|
||||||
var app = builder.Build();
|
opt.MultipartBodyLengthLimit = long.MaxValue;
|
||||||
|
});
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
if (!app.Environment.IsDevelopment())
|
var app = builder.Build();
|
||||||
{
|
|
||||||
app.UseExceptionHandler("/Home/Error");
|
// Configure the HTTP request pipeline.
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
if (!app.Environment.IsDevelopment())
|
||||||
app.UseHsts();
|
{
|
||||||
}
|
app.UseExceptionHandler("/Home/Error");
|
||||||
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||||
app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true });
|
app.UseHsts();
|
||||||
app.UseStaticFiles();
|
}
|
||||||
app.UseRouting();
|
|
||||||
|
app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true });
|
||||||
|
app.UseStaticFiles();
|
||||||
app.UseCors();
|
app.UseRouting();
|
||||||
|
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseCors();
|
||||||
app.UseAuthorization();
|
|
||||||
|
|
||||||
app.MapControllers();
|
app.UseAuthentication();
|
||||||
app.MapObserability();
|
app.UseAuthorization();
|
||||||
app.MapGrpcService<AobaRpcService>()
|
|
||||||
.RequireAuthorization()
|
app.MapControllers();
|
||||||
.RequireCors("RPC");
|
app.MapObserability();
|
||||||
app.MapGrpcService<MetricsRpcService>()
|
app.MapGrpcService<AobaRpcService>()
|
||||||
.RequireAuthorization()
|
.RequireAuthorization()
|
||||||
.RequireCors("RPC");
|
.RequireCors("RPC");
|
||||||
app.MapGrpcService<AobaAuthService>()
|
app.MapGrpcService<MetricsRpcService>()
|
||||||
.AllowAnonymous()
|
.RequireAuthorization()
|
||||||
.RequireCors("RPC");
|
.RequireCors("RPC");
|
||||||
app.MapFallbackToFile("index.html");
|
app.MapGrpcService<AobaAuthService>()
|
||||||
|
.AllowAnonymous()
|
||||||
app.Run();
|
.RequireCors("RPC");
|
||||||
|
app.MapFallbackToFile("index.html");
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|||||||
19
AobaServer/Services/DebugService.cs
Normal file
19
AobaServer/Services/DebugService.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
using AobaCore.Services;
|
||||||
|
|
||||||
|
namespace AobaServer.Services;
|
||||||
|
|
||||||
|
public class DebugService(AobaService aobaService, ThumbnailService thumbnailService) : BackgroundService
|
||||||
|
{
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
var mediaItems = await aobaService.FindMediaWithExtAsync(".avif", stoppingToken);
|
||||||
|
foreach (var item in mediaItems)
|
||||||
|
{
|
||||||
|
foreach (var size in item.Thumbnails.Keys)
|
||||||
|
{
|
||||||
|
await thumbnailService.DeleteThumbnailAsync(item.MediaId, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user