mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-06 19:25:22 +03:00
remove CORS and add CSRF token
This commit is contained in:
parent
62a5cb87be
commit
86576889b1
@ -93,10 +93,6 @@ http {
|
|||||||
secure_token $args;
|
secure_token $args;
|
||||||
secure_token_types application/vnd.apple.mpegurl;
|
secure_token_types application/vnd.apple.mpegurl;
|
||||||
|
|
||||||
add_header Access-Control-Allow-Headers '*';
|
|
||||||
add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range';
|
|
||||||
add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
|
|
||||||
add_header Access-Control-Allow-Origin '*';
|
|
||||||
add_header Cache-Control "no-store";
|
add_header Cache-Control "no-store";
|
||||||
expires off;
|
expires off;
|
||||||
}
|
}
|
||||||
@ -104,16 +100,6 @@ http {
|
|||||||
location /stream/ {
|
location /stream/ {
|
||||||
add_header Cache-Control "no-store";
|
add_header Cache-Control "no-store";
|
||||||
expires off;
|
expires off;
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
|
|
||||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
|
||||||
add_header 'Access-Control-Expose-Headers' 'Content-Length';
|
|
||||||
if ($request_method = 'OPTIONS') {
|
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin";
|
|
||||||
add_header 'Access-Control-Max-Age' 1728000;
|
|
||||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
|
||||||
add_header 'Content-Length' 0;
|
|
||||||
return 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
types {
|
types {
|
||||||
application/dash+xml mpd;
|
application/dash+xml mpd;
|
||||||
@ -126,16 +112,6 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location /clips/ {
|
location /clips/ {
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
|
|
||||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
|
||||||
add_header 'Access-Control-Expose-Headers' 'Content-Length';
|
|
||||||
if ($request_method = 'OPTIONS') {
|
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin";
|
|
||||||
add_header 'Access-Control-Max-Age' 1728000;
|
|
||||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
|
||||||
add_header 'Content-Length' 0;
|
|
||||||
return 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
types {
|
types {
|
||||||
video/mp4 mp4;
|
video/mp4 mp4;
|
||||||
@ -152,17 +128,6 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location /recordings/ {
|
location /recordings/ {
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
|
|
||||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
|
||||||
add_header 'Access-Control-Expose-Headers' 'Content-Length';
|
|
||||||
if ($request_method = 'OPTIONS') {
|
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin";
|
|
||||||
add_header 'Access-Control-Max-Age' 1728000;
|
|
||||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
|
||||||
add_header 'Content-Length' 0;
|
|
||||||
return 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
types {
|
types {
|
||||||
video/mp4 mp4;
|
video/mp4 mp4;
|
||||||
}
|
}
|
||||||
@ -173,17 +138,6 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location /exports/ {
|
location /exports/ {
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
|
|
||||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
|
||||||
add_header 'Access-Control-Expose-Headers' 'Content-Length';
|
|
||||||
if ($request_method = 'OPTIONS') {
|
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin";
|
|
||||||
add_header 'Access-Control-Max-Age' 1728000;
|
|
||||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
|
||||||
add_header 'Content-Length' 0;
|
|
||||||
return 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
types {
|
types {
|
||||||
video/mp4 mp4;
|
video/mp4 mp4;
|
||||||
}
|
}
|
||||||
@ -235,8 +189,6 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location ~* /api/.*\.(jpg|jpeg|png)$ {
|
location ~* /api/.*\.(jpg|jpeg|png)$ {
|
||||||
add_header 'Access-Control-Allow-Origin' '*';
|
|
||||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
|
|
||||||
rewrite ^/api/(.*)$ $1 break;
|
rewrite ^/api/(.*)$ $1 break;
|
||||||
proxy_pass http://frigate_api;
|
proxy_pass http://frigate_api;
|
||||||
proxy_pass_request_headers on;
|
proxy_pass_request_headers on;
|
||||||
@ -248,10 +200,6 @@ http {
|
|||||||
location /api/ {
|
location /api/ {
|
||||||
add_header Cache-Control "no-store";
|
add_header Cache-Control "no-store";
|
||||||
expires off;
|
expires off;
|
||||||
|
|
||||||
add_header 'Access-Control-Allow-Origin' '*';
|
|
||||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
|
|
||||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
|
||||||
proxy_pass http://frigate_api/;
|
proxy_pass http://frigate_api/;
|
||||||
proxy_pass_request_headers on;
|
proxy_pass_request_headers on;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
|||||||
@ -155,10 +155,6 @@ cd web && npm install
|
|||||||
cd web && npm run dev
|
cd web && npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3a. Run the development server against a non-local instance
|
|
||||||
|
|
||||||
To run the development server against a non-local instance, you will need to modify the API_HOST default return in `web/src/env.js`.
|
|
||||||
|
|
||||||
#### 4. Making changes
|
#### 4. Making changes
|
||||||
|
|
||||||
The Web UI is built using [Vite](https://vitejs.dev/), [Preact](https://preactjs.com), and [Tailwind CSS](https://tailwindcss.com).
|
The Web UI is built using [Vite](https://vitejs.dev/), [Preact](https://preactjs.com), and [Tailwind CSS](https://tailwindcss.com).
|
||||||
|
|||||||
@ -74,6 +74,13 @@ def create_app(
|
|||||||
):
|
):
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def check_csrf():
|
||||||
|
if request.method in ["GET", "HEAD", "OPTIONS", "TRACE"]:
|
||||||
|
pass
|
||||||
|
if "origin" in request.headers and "x-csrf-token" not in request.headers:
|
||||||
|
return jsonify({"success": False, "message": "Missing CSRF header"}), 401
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def _db_connect():
|
def _db_connect():
|
||||||
if database.is_closed():
|
if database.is_closed():
|
||||||
|
|||||||
@ -20,7 +20,6 @@
|
|||||||
},
|
},
|
||||||
"ignorePatterns": ["*.d.ts"],
|
"ignorePatterns": ["*.d.ts"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
|
||||||
"comma-dangle": [
|
"comma-dangle": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"printWidth": 120,
|
"printWidth": 120,
|
||||||
|
"trailingComma": "es5",
|
||||||
"singleQuote": true
|
"singleQuote": true
|
||||||
}
|
}
|
||||||
|
|||||||
1290
web/package-lock.json
generated
1290
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -45,6 +45,7 @@
|
|||||||
"eslint-config-preact": "^1.3.0",
|
"eslint-config-preact": "^1.3.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-jest": "^27.2.3",
|
"eslint-plugin-jest": "^27.2.3",
|
||||||
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"eslint-plugin-vitest-globals": "^1.4.0",
|
"eslint-plugin-vitest-globals": "^1.4.0",
|
||||||
"fake-indexeddb": "^4.0.1",
|
"fake-indexeddb": "^4.0.1",
|
||||||
"jsdom": "^22.0.0",
|
"jsdom": "^22.0.0",
|
||||||
|
|||||||
@ -1,2 +1 @@
|
|||||||
import { API_HOST } from '../env';
|
export const baseUrl = `${window.location.protocol}//${window.location.host}${window.baseUrl || '/'}`;
|
||||||
export const baseUrl = API_HOST || `${window.location.protocol}//${window.location.host}${window.baseUrl || '/'}`;
|
|
||||||
|
|||||||
@ -5,6 +5,9 @@ import { WsProvider } from './ws';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
axios.defaults.baseURL = `${baseUrl}api/`;
|
axios.defaults.baseURL = `${baseUrl}api/`;
|
||||||
|
axios.defaults.headers.common = {
|
||||||
|
'X-CSRF-TOKEN': 1,
|
||||||
|
};
|
||||||
|
|
||||||
export function ApiProvider({ children, options }) {
|
export function ApiProvider({ children, options }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -58,7 +58,7 @@ export default function CameraImage({ camera, onload, searchParams = '', stretch
|
|||||||
if (!config || scaledHeight === 0 || !canvasRef.current) {
|
if (!config || scaledHeight === 0 || !canvasRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
img.src = `${apiHost}/api/${name}/latest.jpg?h=${scaledHeight}${searchParams ? `&${searchParams}` : ''}`;
|
img.src = `${apiHost}api/${name}/latest.jpg?h=${scaledHeight}${searchParams ? `&${searchParams}` : ''}`;
|
||||||
}, [apiHost, canvasRef, name, img, searchParams, scaledHeight, config]);
|
}, [apiHost, canvasRef, name, img, searchParams, scaledHeight, config]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -56,10 +56,10 @@ export const HistoryVideo = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
video.src({
|
video.src({
|
||||||
src: `${apiHost}/vod/event/${id}/master.m3u8`,
|
src: `${apiHost}vod/event/${id}/master.m3u8`,
|
||||||
type: 'application/vnd.apple.mpegurl',
|
type: 'application/vnd.apple.mpegurl',
|
||||||
});
|
});
|
||||||
video.poster(`${apiHost}/api/events/${id}/snapshot.jpg`);
|
video.poster(`${apiHost}api/events/${id}/snapshot.jpg`);
|
||||||
if (videoIsPlaying) {
|
if (videoIsPlaying) {
|
||||||
video.play();
|
video.play();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -153,7 +153,7 @@ export function EventCard({ camera, event }) {
|
|||||||
<Link className="" href={`/recording/${camera}/${format(start, 'yyyy-MM-dd/HH/mm/ss')}`}>
|
<Link className="" href={`/recording/${camera}/${format(start, 'yyyy-MM-dd/HH/mm/ss')}`}>
|
||||||
<div className="flex flex-row mb-2">
|
<div className="flex flex-row mb-2">
|
||||||
<div className="w-28 mr-4">
|
<div className="w-28 mr-4">
|
||||||
<img className="antialiased" loading="lazy" src={`${apiHost}/api/events/${event.id}/thumbnail.jpg`} />
|
<img className="antialiased" loading="lazy" src={`${apiHost}api/events/${event.id}/thumbnail.jpg`} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row w-full border-b">
|
<div className="flex flex-row w-full border-b">
|
||||||
<div className="w-full text-gray-700 font-semibold relative pt-0">
|
<div className="w-full text-gray-700 font-semibold relative pt-0">
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
export const ENV = import.meta.env.MODE;
|
|
||||||
export const API_HOST = ENV === 'production' ? '' : 'http://localhost:5000/';
|
|
||||||
@ -212,7 +212,7 @@ export default function Camera({ camera }) {
|
|||||||
key={objectType}
|
key={objectType}
|
||||||
header={objectType}
|
header={objectType}
|
||||||
href={`/events?cameras=${camera}&labels=${encodeURIComponent(objectType)}`}
|
href={`/events?cameras=${camera}&labels=${encodeURIComponent(objectType)}`}
|
||||||
media={<img src={`${apiHost}/api/${camera}/${encodeURIComponent(objectType)}/thumbnail.jpg`} />}
|
media={<img src={`${apiHost}api/${camera}/${encodeURIComponent(objectType)}/thumbnail.jpg`} />}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -336,7 +336,7 @@ ${Object.keys(objectMaskPoints)
|
|||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<img ref={imageRef} src={`${apiHost}/api/${camera}/latest.jpg`} />
|
<img ref={imageRef} src={`${apiHost}api/${camera}/latest.jpg`} />
|
||||||
<EditableMask
|
<EditableMask
|
||||||
onChange={handleUpdateEditable}
|
onChange={handleUpdateEditable}
|
||||||
points={'subkey' in editing ? editing.set[editing.key][editing.subkey] : editing.set[editing.key]}
|
points={'subkey' in editing ? editing.set[editing.key][editing.subkey] : editing.set[editing.key]}
|
||||||
|
|||||||
@ -74,7 +74,7 @@ export default function Config() {
|
|||||||
format: true,
|
format: true,
|
||||||
schemas: [
|
schemas: [
|
||||||
{
|
{
|
||||||
uri: `${apiHost}/api/config/schema.json`,
|
uri: `${apiHost}api/config/schema.json`,
|
||||||
fileMatch: [String(modelUri)],
|
fileMatch: [String(modelUri)],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -402,7 +402,7 @@ export default function Events({ path, ...props }) {
|
|||||||
icon={Snapshot}
|
icon={Snapshot}
|
||||||
label="Download Snapshot"
|
label="Download Snapshot"
|
||||||
value="snapshot"
|
value="snapshot"
|
||||||
href={`${apiHost}/api/events/${downloadEvent.id}/snapshot.jpg?download=true`}
|
href={`${apiHost}api/events/${downloadEvent.id}/snapshot.jpg?download=true`}
|
||||||
download
|
download
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -411,7 +411,7 @@ export default function Events({ path, ...props }) {
|
|||||||
icon={Clip}
|
icon={Clip}
|
||||||
label="Download Clip"
|
label="Download Clip"
|
||||||
value="clip"
|
value="clip"
|
||||||
href={`${apiHost}/api/events/${downloadEvent.id}/clip.mp4?download=true`}
|
href={`${apiHost}api/events/${downloadEvent.id}/clip.mp4?download=true`}
|
||||||
download
|
download
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -492,7 +492,7 @@ export default function Events({ path, ...props }) {
|
|||||||
|
|
||||||
<img
|
<img
|
||||||
className="flex-grow-0"
|
className="flex-grow-0"
|
||||||
src={`${apiHost}/api/events/${plusSubmitEvent.id}/snapshot.jpg`}
|
src={`${apiHost}api/events/${plusSubmitEvent.id}/snapshot.jpg`}
|
||||||
alt={`${plusSubmitEvent.label}`}
|
alt={`${plusSubmitEvent.label}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -619,7 +619,7 @@ export default function Events({ path, ...props }) {
|
|||||||
<div
|
<div
|
||||||
className="relative rounded-l flex-initial min-w-[125px] h-[125px] bg-contain bg-no-repeat bg-center"
|
className="relative rounded-l flex-initial min-w-[125px] h-[125px] bg-contain bg-no-repeat bg-center"
|
||||||
style={{
|
style={{
|
||||||
'background-image': `url(${apiHost}/api/events/${event.id}/thumbnail.jpg)`,
|
'background-image': `url(${apiHost}api/events/${event.id}/thumbnail.jpg)`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StarRecording
|
<StarRecording
|
||||||
@ -776,8 +776,8 @@ export default function Events({ path, ...props }) {
|
|||||||
className="flex-grow-0"
|
className="flex-grow-0"
|
||||||
src={
|
src={
|
||||||
event.has_snapshot
|
event.has_snapshot
|
||||||
? `${apiHost}/api/events/${event.id}/snapshot.jpg`
|
? `${apiHost}api/events/${event.id}/snapshot.jpg`
|
||||||
: `${apiHost}/api/events/${event.id}/thumbnail.jpg`
|
: `${apiHost}api/events/${event.id}/thumbnail.jpg`
|
||||||
}
|
}
|
||||||
alt={`${event.label} at ${((event?.data?.top_score || event.top_score) * 100).toFixed(
|
alt={`${event.label} at ${((event?.data?.top_score || event.top_score) * 100).toFixed(
|
||||||
0
|
0
|
||||||
|
|||||||
@ -9,6 +9,13 @@ export default defineConfig({
|
|||||||
define: {
|
define: {
|
||||||
'import.meta.vitest': 'undefined',
|
'import.meta.vitest': 'undefined',
|
||||||
},
|
},
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:5000'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
preact(),
|
preact(),
|
||||||
monacoEditorPlugin.default({
|
monacoEditorPlugin.default({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user