forked from Kieran/void.cat
Add CORS to api
This commit is contained in:
parent
36d5db3f29
commit
67b5ef2b10
@ -75,7 +75,8 @@ public class AuthController : Controller
|
|||||||
var claims = new List<Claim>()
|
var claims = new List<Claim>()
|
||||||
{
|
{
|
||||||
new(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
new(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||||
new(ClaimTypes.Expiration, DateTimeOffset.UtcNow.AddHours(6).ToUnixTimeSeconds().ToString())
|
new(JwtRegisteredClaimNames.Exp, DateTimeOffset.UtcNow.AddHours(6).ToUnixTimeSeconds().ToString()),
|
||||||
|
new(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())
|
||||||
};
|
};
|
||||||
claims.AddRange(user.Roles.Select(a => new Claim(ClaimTypes.Role, a)));
|
claims.AddRange(user.Roles.Select(a => new Claim(ClaimTypes.Role, a)));
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Prometheus;
|
|
||||||
using VoidCat.Model;
|
using VoidCat.Model;
|
||||||
using VoidCat.Services;
|
|
||||||
using VoidCat.Services.Abstractions;
|
using VoidCat.Services.Abstractions;
|
||||||
|
|
||||||
namespace VoidCat.Controllers
|
namespace VoidCat.Controllers
|
||||||
@ -19,7 +17,7 @@ namespace VoidCat.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ResponseCache(Location = ResponseCacheLocation.Client, Duration = 60)]
|
[ResponseCache(Location = ResponseCacheLocation.Any, Duration = 60)]
|
||||||
public async Task<GlobalStats> GetGlobalStats()
|
public async Task<GlobalStats> GetGlobalStats()
|
||||||
{
|
{
|
||||||
var bw = await _statsReporter.GetBandwidth();
|
var bw = await _statsReporter.GetBandwidth();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Cors;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -8,6 +9,7 @@ using VoidCat.Services.Abstractions;
|
|||||||
|
|
||||||
namespace VoidCat.Controllers
|
namespace VoidCat.Controllers
|
||||||
{
|
{
|
||||||
|
[EnableCors(CorsPolicy.Upload)]
|
||||||
[Route("upload")]
|
[Route("upload")]
|
||||||
public class UploadController : Controller
|
public class UploadController : Controller
|
||||||
{
|
{
|
||||||
@ -25,14 +27,6 @@ namespace VoidCat.Controllers
|
|||||||
_paywallFactory = paywallFactory;
|
_paywallFactory = paywallFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpOptions]
|
|
||||||
public IActionResult UploadFileOptions()
|
|
||||||
{
|
|
||||||
// just return 200 status for pre-flight calls
|
|
||||||
// manging CORS headers is not managed inside void.cat
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[DisableRequestSizeLimit]
|
[DisableRequestSizeLimit]
|
||||||
[DisableFormValueModelBinding]
|
[DisableFormValueModelBinding]
|
||||||
|
@ -10,3 +10,9 @@ public static class Policies
|
|||||||
{
|
{
|
||||||
public const string RequireAdmin = "RequireAdmin";
|
public const string RequireAdmin = "RequireAdmin";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CorsPolicy
|
||||||
|
{
|
||||||
|
public const string Default = "default";
|
||||||
|
public const string Upload = "upload";
|
||||||
|
}
|
@ -15,6 +15,8 @@ namespace VoidCat.Model
|
|||||||
public StrikeApiSettings? Strike { get; init; }
|
public StrikeApiSettings? Strike { get; init; }
|
||||||
|
|
||||||
public SmtpSettings? Smtp { get; init; }
|
public SmtpSettings? Smtp { get; init; }
|
||||||
|
|
||||||
|
public List<Uri> CorsOrigins { get; init; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed record TorSettings(Uri TorControl, string PrivateKey, string ControlPassword);
|
public sealed record TorSettings(Uri TorControl, string PrivateKey, string ControlPassword);
|
||||||
|
@ -33,6 +33,24 @@ if (useRedis)
|
|||||||
services.AddSingleton(cx.GetDatabase());
|
services.AddSingleton(cx.GetDatabase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
services.AddCors(opt =>
|
||||||
|
{
|
||||||
|
opt.AddPolicy(CorsPolicy.Default, p =>
|
||||||
|
{
|
||||||
|
p.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.WithOrigins(voidSettings.CorsOrigins.Select(a => a.OriginalString).ToArray());
|
||||||
|
});
|
||||||
|
|
||||||
|
opt.AddPolicy(CorsPolicy.Upload, p =>
|
||||||
|
{
|
||||||
|
p.AllowCredentials()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.WithHeaders("V-Content-Type", "V-Filename", "V-Digest", "Content-Type", "Authorization")
|
||||||
|
.WithOrigins(voidSettings.CorsOrigins.Select(a => a.OriginalString).ToArray());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
services.AddRouting();
|
services.AddRouting();
|
||||||
services.AddControllers().AddNewtonsoftJson((opt) =>
|
services.AddControllers().AddNewtonsoftJson((opt) =>
|
||||||
{
|
{
|
||||||
@ -112,6 +130,7 @@ app.UseStaticFiles();
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
app.UseCors(CorsPolicy.Default);
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
import {useDispatch} from "react-redux";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {FormatBytes} from "../Util";
|
import {FormatBytes} from "../Util";
|
||||||
import {useApi} from "../Api";
|
import {useApi} from "../Api";
|
||||||
@ -8,9 +8,8 @@ import {logout} from "../LoginState";
|
|||||||
import {PagedSortBy, PageSortOrder} from "../Const";
|
import {PagedSortBy, PageSortOrder} from "../Const";
|
||||||
import {PageSelector} from "../PageSelector";
|
import {PageSelector} from "../PageSelector";
|
||||||
|
|
||||||
export function FileList(props) {
|
export function FileList() {
|
||||||
const {AdminApi} = useApi();
|
const {AdminApi} = useApi();
|
||||||
const auth = useSelector((state) => state.login.jwt);
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [files, setFiles] = useState();
|
const [files, setFiles] = useState();
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
@ -24,7 +23,7 @@ export function FileList(props) {
|
|||||||
sortBy: PagedSortBy.Date,
|
sortBy: PagedSortBy.Date,
|
||||||
sortOrder: PageSortOrder.Dsc
|
sortOrder: PageSortOrder.Dsc
|
||||||
};
|
};
|
||||||
let req = await AdminApi.fileList(auth, pageReq);
|
let req = await AdminApi.fileList(pageReq);
|
||||||
if (req.ok) {
|
if (req.ok) {
|
||||||
setFiles(await req.json());
|
setFiles(await req.json());
|
||||||
} else if (req.status === 401) {
|
} else if (req.status === 401) {
|
||||||
@ -37,7 +36,7 @@ export function FileList(props) {
|
|||||||
async function deleteFile(e, id) {
|
async function deleteFile(e, id) {
|
||||||
e.target.disabled = true;
|
e.target.disabled = true;
|
||||||
if (window.confirm(`Are you sure you want to delete: ${id}?`)) {
|
if (window.confirm(`Are you sure you want to delete: ${id}?`)) {
|
||||||
let req = await AdminApi.deleteFile(auth, id);
|
let req = await AdminApi.deleteFile(id);
|
||||||
if (req.ok) {
|
if (req.ok) {
|
||||||
setFiles({
|
setFiles({
|
||||||
...files,
|
...files,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {useDispatch, useSelector} from "react-redux";
|
import {useDispatch} from "react-redux";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {PagedSortBy, PageSortOrder} from "../Const";
|
import {PagedSortBy, PageSortOrder} from "../Const";
|
||||||
import {useApi} from "../Api";
|
import {useApi} from "../Api";
|
||||||
@ -8,7 +8,6 @@ import moment from "moment";
|
|||||||
|
|
||||||
export function UserList() {
|
export function UserList() {
|
||||||
const {AdminApi} = useApi();
|
const {AdminApi} = useApi();
|
||||||
const auth = useSelector((state) => state.login.jwt);
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [users, setUsers] = useState();
|
const [users, setUsers] = useState();
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
@ -22,12 +21,12 @@ export function UserList() {
|
|||||||
sortBy: PagedSortBy.Id,
|
sortBy: PagedSortBy.Id,
|
||||||
sortOrder: PageSortOrder.Asc
|
sortOrder: PageSortOrder.Asc
|
||||||
};
|
};
|
||||||
let req = await AdminApi.userList(auth, pageReq);
|
let req = await AdminApi.userList(pageReq);
|
||||||
if (req.ok) {
|
if (req.ok) {
|
||||||
setUsers(await req.json());
|
setUsers(await req.json());
|
||||||
} else if (req.status === 401) {
|
} else if (req.status === 401) {
|
||||||
dispatch(logout());
|
dispatch(logout());
|
||||||
} else if(req.status === 403) {
|
} else if (req.status === 403) {
|
||||||
setAccessDenied(true);
|
setAccessDenied(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@ import {ApiHost} from "./Const";
|
|||||||
export function useApi() {
|
export function useApi() {
|
||||||
const auth = useSelector(state => state.login.jwt);
|
const auth = useSelector(state => state.login.jwt);
|
||||||
|
|
||||||
async function getJson(method, url, body) {
|
async function getJson(method, url, body, token) {
|
||||||
let headers = {
|
let headers = {
|
||||||
"Accept": "application/json"
|
"Accept": "application/json"
|
||||||
};
|
};
|
||||||
if (auth) {
|
if (token) {
|
||||||
headers["Authorization"] = `Bearer ${auth}`;
|
headers["Authorization"] = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
if (body) {
|
if (body) {
|
||||||
headers["Content-Type"] = "application/json";
|
headers["Content-Type"] = "application/json";
|
||||||
@ -18,20 +18,21 @@ export function useApi() {
|
|||||||
return await fetch(`${ApiHost}${url}`, {
|
return await fetch(`${ApiHost}${url}`, {
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
|
mode: "cors",
|
||||||
body: body ? JSON.stringify(body) : undefined
|
body: body ? JSON.stringify(body) : undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
AdminApi: {
|
AdminApi: {
|
||||||
fileList: (pageReq) => getJson("POST", "/admin/file", pageReq),
|
fileList: (pageReq) => getJson("POST", "/admin/file", pageReq, auth),
|
||||||
deleteFile: (id) => getJson("DELETE", `/admin/file/${id}`),
|
deleteFile: (id) => getJson("DELETE", `/admin/file/${id}`, undefined, auth),
|
||||||
userList: (pageReq) => getJson("POST", `/admin/user`, pageReq)
|
userList: (pageReq) => getJson("POST", `/admin/user`, pageReq, auth)
|
||||||
},
|
},
|
||||||
Api: {
|
Api: {
|
||||||
stats: () => getJson("GET", "/stats"),
|
stats: () => getJson("GET", "/stats"),
|
||||||
fileInfo: (id) => getJson("GET", `/upload/${id}`),
|
fileInfo: (id) => getJson("GET", `/upload/${id}`),
|
||||||
setPaywallConfig: (id, cfg) => getJson("POST", `/upload/${id}/paywall`, cfg),
|
setPaywallConfig: (id, cfg) => getJson("POST", `/upload/${id}/paywall`, cfg, auth),
|
||||||
createOrder: (id) => getJson("GET", `/upload/${id}/paywall`),
|
createOrder: (id) => getJson("GET", `/upload/${id}/paywall`),
|
||||||
getOrder: (file, order) => getJson("GET", `/upload/${file}/paywall/${order}`),
|
getOrder: (file, order) => getJson("GET", `/upload/${file}/paywall/${order}`),
|
||||||
login: (username, password) => getJson("POST", `/auth/login`, {username, password}),
|
login: (username, password) => getJson("POST", `/auth/login`, {username, password}),
|
||||||
|
Loading…
Reference in New Issue
Block a user