@adventurelabs/scout-core 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants/db.d.ts +1 -0
- package/dist/constants/db.js +1 -0
- package/dist/helpers/events.js +11 -2
- package/dist/helpers/storage.d.ts +36 -0
- package/dist/helpers/storage.js +82 -0
- package/dist/helpers/tags.js +10 -3
- package/dist/hooks/useSignedUrl.d.ts +11 -0
- package/dist/hooks/useSignedUrl.js +50 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/supabase/server.d.ts +9 -4
- package/dist/types/db.js +1 -0
- package/dist/types/supabase.d.ts +9 -4
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const BUCKET_NAME_SCOUT = "scout";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const BUCKET_NAME_SCOUT = "scout";
|
package/dist/helpers/events.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
import { newServerClient } from "../supabase/server";
|
|
3
3
|
import { EnumWebResponse, IWebResponse, } from "../types/requests";
|
|
4
|
+
import { addSignedUrlsToEvents } from "./storage";
|
|
4
5
|
export async function server_get_events_by_herd(herd_id) {
|
|
5
6
|
const supabase = await newServerClient();
|
|
6
7
|
// fetch events and include devices
|
|
@@ -13,8 +14,12 @@ export async function server_get_events_by_herd(herd_id) {
|
|
|
13
14
|
`)
|
|
14
15
|
.eq("devices.herd_id", herd_id)
|
|
15
16
|
.order("timestamp_observation", { ascending: false });
|
|
17
|
+
// Add signed URLs to events using the same client
|
|
18
|
+
const eventsWithSignedUrls = data
|
|
19
|
+
? await addSignedUrlsToEvents(data, supabase)
|
|
20
|
+
: [];
|
|
16
21
|
// TODO: DETERMINE WHEN TO PASS ERROR
|
|
17
|
-
let response = IWebResponse.success(
|
|
22
|
+
let response = IWebResponse.success(eventsWithSignedUrls);
|
|
18
23
|
return response.to_compatible();
|
|
19
24
|
}
|
|
20
25
|
export async function server_get_more_events_by_herd(herd_id, offset, page_count = 10) {
|
|
@@ -32,8 +37,12 @@ export async function server_get_more_events_by_herd(herd_id, offset, page_count
|
|
|
32
37
|
.eq("devices.herd_id", herd_id)
|
|
33
38
|
.range(from, to)
|
|
34
39
|
.order("timestamp_observation", { ascending: false });
|
|
40
|
+
// Add signed URLs to events using the same client
|
|
41
|
+
const eventsWithSignedUrls = data
|
|
42
|
+
? await addSignedUrlsToEvents(data, supabase)
|
|
43
|
+
: [];
|
|
35
44
|
// TODO: DETERMINE WHEN TO PASS ERROR
|
|
36
|
-
let response = IWebResponse.success(
|
|
45
|
+
let response = IWebResponse.success(eventsWithSignedUrls);
|
|
37
46
|
return response.to_compatible();
|
|
38
47
|
}
|
|
39
48
|
// function to get total number of events for a herd
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { Database } from "../types/supabase";
|
|
3
|
+
/**
|
|
4
|
+
* Generates a signed URL for a file in Supabase storage
|
|
5
|
+
* @param filePath - The path to the file in storage (e.g., "events/123/image.jpg")
|
|
6
|
+
* @param expiresIn - Number of seconds until the URL expires (default: 3600 = 1 hour)
|
|
7
|
+
* @param supabaseClient - Optional Supabase client (will create new one if not provided)
|
|
8
|
+
* @returns Promise<string | null> - The signed URL or null if error
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateSignedUrl(filePath: string, expiresIn?: number, supabaseClient?: SupabaseClient<Database>): Promise<string | null>;
|
|
11
|
+
/**
|
|
12
|
+
* Generates signed URLs for multiple events and sets them as media_url
|
|
13
|
+
* @param events - Array of events that may have file_path
|
|
14
|
+
* @param supabaseClient - Optional Supabase client (will create new one if not provided)
|
|
15
|
+
* @returns Promise<Array> - Events with signed URLs set as media_url
|
|
16
|
+
*/
|
|
17
|
+
export declare function addSignedUrlsToEvents(events: any[], supabaseClient?: SupabaseClient<Database>): Promise<any[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Generates a signed URL for a single event and sets it as media_url
|
|
20
|
+
* @param event - Event object that may have file_path
|
|
21
|
+
* @param supabaseClient - Optional Supabase client (will create new one if not provided)
|
|
22
|
+
* @returns Promise<Object> - Event with signed URL set as media_url
|
|
23
|
+
*/
|
|
24
|
+
export declare function addSignedUrlToEvent(event: any, supabaseClient?: SupabaseClient<Database>): Promise<any>;
|
|
25
|
+
/**
|
|
26
|
+
* Gets the media URL for an event (signed URL if file_path exists, otherwise media_url)
|
|
27
|
+
* @param event - Event object that may have file_path or media_url
|
|
28
|
+
* @returns string | null - The media URL or null if none available
|
|
29
|
+
*/
|
|
30
|
+
export declare function getEventMediaUrl(event: any): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Checks if an event has any media URL available
|
|
33
|
+
* @param event - Event object
|
|
34
|
+
* @returns boolean - True if event has any media URL
|
|
35
|
+
*/
|
|
36
|
+
export declare function hasEventMedia(event: any): boolean;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import { newServerClient } from "../supabase/server";
|
|
3
|
+
import { BUCKET_NAME_SCOUT } from "../constants/db";
|
|
4
|
+
/**
|
|
5
|
+
* Generates a signed URL for a file in Supabase storage
|
|
6
|
+
* @param filePath - The path to the file in storage (e.g., "events/123/image.jpg")
|
|
7
|
+
* @param expiresIn - Number of seconds until the URL expires (default: 3600 = 1 hour)
|
|
8
|
+
* @param supabaseClient - Optional Supabase client (will create new one if not provided)
|
|
9
|
+
* @returns Promise<string | null> - The signed URL or null if error
|
|
10
|
+
*/
|
|
11
|
+
export async function generateSignedUrl(filePath, expiresIn = 3600, supabaseClient) {
|
|
12
|
+
try {
|
|
13
|
+
const supabase = supabaseClient || (await newServerClient());
|
|
14
|
+
const { data, error } = await supabase.storage
|
|
15
|
+
.from(BUCKET_NAME_SCOUT)
|
|
16
|
+
.createSignedUrl(filePath, expiresIn);
|
|
17
|
+
if (error) {
|
|
18
|
+
console.error("Error generating signed URL:", error.message);
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return data.signedUrl;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error("Error in generateSignedUrl:", error);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Generates signed URLs for multiple events and sets them as media_url
|
|
30
|
+
* @param events - Array of events that may have file_path
|
|
31
|
+
* @param supabaseClient - Optional Supabase client (will create new one if not provided)
|
|
32
|
+
* @returns Promise<Array> - Events with signed URLs set as media_url
|
|
33
|
+
*/
|
|
34
|
+
export async function addSignedUrlsToEvents(events, supabaseClient) {
|
|
35
|
+
const eventsWithSignedUrls = await Promise.all(events.map(async (event) => {
|
|
36
|
+
// If event has a file_path, generate a signed URL and set it as media_url
|
|
37
|
+
if (event.file_path) {
|
|
38
|
+
const signedUrl = await generateSignedUrl(event.file_path, 3600, supabaseClient);
|
|
39
|
+
return {
|
|
40
|
+
...event,
|
|
41
|
+
media_url: signedUrl || event.media_url, // Fall back to existing media_url if signed URL fails
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// If no file_path, keep existing media_url
|
|
45
|
+
return event;
|
|
46
|
+
}));
|
|
47
|
+
return eventsWithSignedUrls;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Generates a signed URL for a single event and sets it as media_url
|
|
51
|
+
* @param event - Event object that may have file_path
|
|
52
|
+
* @param supabaseClient - Optional Supabase client (will create new one if not provided)
|
|
53
|
+
* @returns Promise<Object> - Event with signed URL set as media_url
|
|
54
|
+
*/
|
|
55
|
+
export async function addSignedUrlToEvent(event, supabaseClient) {
|
|
56
|
+
// If event has a file_path, generate a signed URL and set it as media_url
|
|
57
|
+
if (event.file_path) {
|
|
58
|
+
const signedUrl = await generateSignedUrl(event.file_path, 3600, supabaseClient);
|
|
59
|
+
return {
|
|
60
|
+
...event,
|
|
61
|
+
media_url: signedUrl || event.media_url, // Fall back to existing media_url if signed URL fails
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// If no file_path, keep existing media_url
|
|
65
|
+
return event;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Gets the media URL for an event (signed URL if file_path exists, otherwise media_url)
|
|
69
|
+
* @param event - Event object that may have file_path or media_url
|
|
70
|
+
* @returns string | null - The media URL or null if none available
|
|
71
|
+
*/
|
|
72
|
+
export function getEventMediaUrl(event) {
|
|
73
|
+
return event.media_url || null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Checks if an event has any media URL available
|
|
77
|
+
* @param event - Event object
|
|
78
|
+
* @returns boolean - True if event has any media URL
|
|
79
|
+
*/
|
|
80
|
+
export function hasEventMedia(event) {
|
|
81
|
+
return !!(event.media_url || event.file_path);
|
|
82
|
+
}
|
package/dist/helpers/tags.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// add tag to db
|
|
3
3
|
import { newServerClient } from "../supabase/server";
|
|
4
4
|
import { EnumWebResponse, IWebResponse, } from "../types/requests";
|
|
5
|
+
import { addSignedUrlsToEvents, addSignedUrlToEvent } from "./storage";
|
|
5
6
|
export async function server_create_tags(tags) {
|
|
6
7
|
const supabase = await newServerClient();
|
|
7
8
|
// remove id key from tags
|
|
@@ -89,7 +90,9 @@ export async function server_get_more_events_with_tags_by_herd(herd_id, offset,
|
|
|
89
90
|
event.tags = event.tags.filter((tag) => tag !== null);
|
|
90
91
|
return event;
|
|
91
92
|
});
|
|
92
|
-
|
|
93
|
+
// Add signed URLs to events using the same client
|
|
94
|
+
const eventsWithSignedUrls = await addSignedUrlsToEvents(filtered_data, supabase);
|
|
95
|
+
return IWebResponse.success(eventsWithSignedUrls).to_compatible();
|
|
93
96
|
}
|
|
94
97
|
export async function server_get_events_and_tags_for_device(device_id, limit = 3) {
|
|
95
98
|
const supabase = await newServerClient();
|
|
@@ -106,7 +109,9 @@ export async function server_get_events_and_tags_for_device(device_id, limit = 3
|
|
|
106
109
|
data: [],
|
|
107
110
|
};
|
|
108
111
|
}
|
|
109
|
-
|
|
112
|
+
// Add signed URLs to events using the same client
|
|
113
|
+
const eventsWithSignedUrls = await addSignedUrlsToEvents(data, supabase);
|
|
114
|
+
return IWebResponse.success(eventsWithSignedUrls).to_compatible();
|
|
110
115
|
}
|
|
111
116
|
export async function get_event_and_tags_by_event_id(event_id) {
|
|
112
117
|
const supabase = await newServerClient();
|
|
@@ -154,5 +159,7 @@ export async function get_event_and_tags_by_event_id(event_id) {
|
|
|
154
159
|
tags: data[0].tags || [],
|
|
155
160
|
earthranger_url: data[0].earthranger_url,
|
|
156
161
|
};
|
|
157
|
-
|
|
162
|
+
// Add signed URL to event using the same client
|
|
163
|
+
const eventWithSignedUrl = await addSignedUrlToEvent(transformedData, supabase);
|
|
164
|
+
return IWebResponse.success(eventWithSignedUrl).to_compatible();
|
|
158
165
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for generating signed URLs on the client side
|
|
3
|
+
* @param filePath - The file path in storage
|
|
4
|
+
* @param expiresIn - Number of seconds until URL expires (default: 3600 = 1 hour)
|
|
5
|
+
* @returns Object with signedUrl and loading state
|
|
6
|
+
*/
|
|
7
|
+
export declare function useSignedUrl(filePath: string | null, expiresIn?: number): {
|
|
8
|
+
signedUrl: string | null;
|
|
9
|
+
loading: boolean;
|
|
10
|
+
error: string | null;
|
|
11
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { createBrowserClient } from "@supabase/ssr";
|
|
4
|
+
import { BUCKET_NAME_EVENTS } from "../constants/db";
|
|
5
|
+
/**
|
|
6
|
+
* Hook for generating signed URLs on the client side
|
|
7
|
+
* @param filePath - The file path in storage
|
|
8
|
+
* @param expiresIn - Number of seconds until URL expires (default: 3600 = 1 hour)
|
|
9
|
+
* @returns Object with signedUrl and loading state
|
|
10
|
+
*/
|
|
11
|
+
export function useSignedUrl(filePath, expiresIn = 3600) {
|
|
12
|
+
const [signedUrl, setSignedUrl] = useState(null);
|
|
13
|
+
const [loading, setLoading] = useState(false);
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!filePath) {
|
|
17
|
+
setSignedUrl(null);
|
|
18
|
+
setError(null);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const generateSignedUrl = async () => {
|
|
22
|
+
setLoading(true);
|
|
23
|
+
setError(null);
|
|
24
|
+
try {
|
|
25
|
+
const url = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
|
|
26
|
+
const anon_key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
|
|
27
|
+
const supabase = createBrowserClient(url, anon_key);
|
|
28
|
+
const { data, error } = await supabase.storage
|
|
29
|
+
.from(BUCKET_NAME_EVENTS)
|
|
30
|
+
.createSignedUrl(filePath, expiresIn);
|
|
31
|
+
if (error) {
|
|
32
|
+
setError(error.message);
|
|
33
|
+
setSignedUrl(null);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
setSignedUrl(data.signedUrl);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
setError(err instanceof Error ? err.message : "Unknown error");
|
|
41
|
+
setSignedUrl(null);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
setLoading(false);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
generateSignedUrl();
|
|
48
|
+
}, [filePath, expiresIn]);
|
|
49
|
+
return { signedUrl, loading, error };
|
|
50
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./constants/annotator";
|
|
2
2
|
export * from "./constants/app";
|
|
3
|
+
export * from "./constants/db";
|
|
3
4
|
export * from "./types/db";
|
|
4
5
|
export * from "./types/herd_module";
|
|
5
6
|
export * from "./types/ui";
|
|
@@ -23,6 +24,7 @@ export * from "./helpers/ui";
|
|
|
23
24
|
export * from "./helpers/users";
|
|
24
25
|
export * from "./helpers/web";
|
|
25
26
|
export * from "./helpers/zones";
|
|
27
|
+
export * from "./helpers/storage";
|
|
26
28
|
export * from "./hooks/useScoutDbListener";
|
|
27
29
|
export * from "./hooks/useScoutRefresh";
|
|
28
30
|
export * from "./providers";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Constants
|
|
2
2
|
export * from "./constants/annotator";
|
|
3
3
|
export * from "./constants/app";
|
|
4
|
+
export * from "./constants/db";
|
|
4
5
|
// Types
|
|
5
6
|
export * from "./types/db";
|
|
6
7
|
export * from "./types/herd_module";
|
|
@@ -26,6 +27,7 @@ export * from "./helpers/ui";
|
|
|
26
27
|
export * from "./helpers/users";
|
|
27
28
|
export * from "./helpers/web";
|
|
28
29
|
export * from "./helpers/zones";
|
|
30
|
+
export * from "./helpers/storage";
|
|
29
31
|
// Hooks
|
|
30
32
|
export * from "./hooks/useScoutDbListener";
|
|
31
33
|
export * from "./hooks/useScoutRefresh";
|
|
@@ -102,13 +102,14 @@ export declare function newServerClient(): Promise<import("@supabase/supabase-js
|
|
|
102
102
|
altitude: number;
|
|
103
103
|
device_id: number;
|
|
104
104
|
earthranger_url: string | null;
|
|
105
|
+
file_path: string | null;
|
|
105
106
|
heading: number;
|
|
106
107
|
id: number;
|
|
107
108
|
inserted_at: string;
|
|
108
109
|
is_public: boolean;
|
|
109
110
|
location: unknown | null;
|
|
110
111
|
media_type: Database["public"]["Enums"]["media_type"];
|
|
111
|
-
media_url: string;
|
|
112
|
+
media_url: string | null;
|
|
112
113
|
message: string;
|
|
113
114
|
timestamp_observation: string;
|
|
114
115
|
};
|
|
@@ -116,13 +117,14 @@ export declare function newServerClient(): Promise<import("@supabase/supabase-js
|
|
|
116
117
|
altitude: number;
|
|
117
118
|
device_id: number;
|
|
118
119
|
earthranger_url?: string | null;
|
|
120
|
+
file_path?: string | null;
|
|
119
121
|
heading: number;
|
|
120
122
|
id?: number;
|
|
121
123
|
inserted_at?: string;
|
|
122
124
|
is_public?: boolean;
|
|
123
125
|
location?: unknown | null;
|
|
124
126
|
media_type?: Database["public"]["Enums"]["media_type"];
|
|
125
|
-
media_url
|
|
127
|
+
media_url?: string | null;
|
|
126
128
|
message: string;
|
|
127
129
|
timestamp_observation?: string;
|
|
128
130
|
};
|
|
@@ -130,13 +132,14 @@ export declare function newServerClient(): Promise<import("@supabase/supabase-js
|
|
|
130
132
|
altitude?: number;
|
|
131
133
|
device_id?: number;
|
|
132
134
|
earthranger_url?: string | null;
|
|
135
|
+
file_path?: string | null;
|
|
133
136
|
heading?: number;
|
|
134
137
|
id?: number;
|
|
135
138
|
inserted_at?: string;
|
|
136
139
|
is_public?: boolean;
|
|
137
140
|
location?: unknown | null;
|
|
138
141
|
media_type?: Database["public"]["Enums"]["media_type"];
|
|
139
|
-
media_url?: string;
|
|
142
|
+
media_url?: string | null;
|
|
140
143
|
message?: string;
|
|
141
144
|
timestamp_observation?: string;
|
|
142
145
|
};
|
|
@@ -362,6 +365,7 @@ export declare function newServerClient(): Promise<import("@supabase/supabase-js
|
|
|
362
365
|
altitude: number | null;
|
|
363
366
|
device_id: number | null;
|
|
364
367
|
earthranger_url: string | null;
|
|
368
|
+
file_path: string | null;
|
|
365
369
|
heading: number | null;
|
|
366
370
|
herd_id: number | null;
|
|
367
371
|
id: number | null;
|
|
@@ -497,13 +501,14 @@ export declare function newServerClient(): Promise<import("@supabase/supabase-js
|
|
|
497
501
|
altitude: number;
|
|
498
502
|
device_id: number;
|
|
499
503
|
earthranger_url: string | null;
|
|
504
|
+
file_path: string | null;
|
|
500
505
|
heading: number;
|
|
501
506
|
id: number;
|
|
502
507
|
inserted_at: string;
|
|
503
508
|
is_public: boolean;
|
|
504
509
|
location: unknown | null;
|
|
505
510
|
media_type: Database["public"]["Enums"]["media_type"];
|
|
506
|
-
media_url: string;
|
|
511
|
+
media_url: string | null;
|
|
507
512
|
message: string;
|
|
508
513
|
timestamp_observation: string;
|
|
509
514
|
}[];
|
package/dist/types/db.js
CHANGED
package/dist/types/supabase.d.ts
CHANGED
|
@@ -136,13 +136,14 @@ export type Database = {
|
|
|
136
136
|
altitude: number;
|
|
137
137
|
device_id: number;
|
|
138
138
|
earthranger_url: string | null;
|
|
139
|
+
file_path: string | null;
|
|
139
140
|
heading: number;
|
|
140
141
|
id: number;
|
|
141
142
|
inserted_at: string;
|
|
142
143
|
is_public: boolean;
|
|
143
144
|
location: unknown | null;
|
|
144
145
|
media_type: Database["public"]["Enums"]["media_type"];
|
|
145
|
-
media_url: string;
|
|
146
|
+
media_url: string | null;
|
|
146
147
|
message: string;
|
|
147
148
|
timestamp_observation: string;
|
|
148
149
|
};
|
|
@@ -150,13 +151,14 @@ export type Database = {
|
|
|
150
151
|
altitude: number;
|
|
151
152
|
device_id: number;
|
|
152
153
|
earthranger_url?: string | null;
|
|
154
|
+
file_path?: string | null;
|
|
153
155
|
heading: number;
|
|
154
156
|
id?: number;
|
|
155
157
|
inserted_at?: string;
|
|
156
158
|
is_public?: boolean;
|
|
157
159
|
location?: unknown | null;
|
|
158
160
|
media_type?: Database["public"]["Enums"]["media_type"];
|
|
159
|
-
media_url
|
|
161
|
+
media_url?: string | null;
|
|
160
162
|
message: string;
|
|
161
163
|
timestamp_observation?: string;
|
|
162
164
|
};
|
|
@@ -164,13 +166,14 @@ export type Database = {
|
|
|
164
166
|
altitude?: number;
|
|
165
167
|
device_id?: number;
|
|
166
168
|
earthranger_url?: string | null;
|
|
169
|
+
file_path?: string | null;
|
|
167
170
|
heading?: number;
|
|
168
171
|
id?: number;
|
|
169
172
|
inserted_at?: string;
|
|
170
173
|
is_public?: boolean;
|
|
171
174
|
location?: unknown | null;
|
|
172
175
|
media_type?: Database["public"]["Enums"]["media_type"];
|
|
173
|
-
media_url?: string;
|
|
176
|
+
media_url?: string | null;
|
|
174
177
|
message?: string;
|
|
175
178
|
timestamp_observation?: string;
|
|
176
179
|
};
|
|
@@ -410,6 +413,7 @@ export type Database = {
|
|
|
410
413
|
altitude: number | null;
|
|
411
414
|
device_id: number | null;
|
|
412
415
|
earthranger_url: string | null;
|
|
416
|
+
file_path: string | null;
|
|
413
417
|
heading: number | null;
|
|
414
418
|
herd_id: number | null;
|
|
415
419
|
id: number | null;
|
|
@@ -550,13 +554,14 @@ export type Database = {
|
|
|
550
554
|
altitude: number;
|
|
551
555
|
device_id: number;
|
|
552
556
|
earthranger_url: string | null;
|
|
557
|
+
file_path: string | null;
|
|
553
558
|
heading: number;
|
|
554
559
|
id: number;
|
|
555
560
|
inserted_at: string;
|
|
556
561
|
is_public: boolean;
|
|
557
562
|
location: unknown | null;
|
|
558
563
|
media_type: Database["public"]["Enums"]["media_type"];
|
|
559
|
-
media_url: string;
|
|
564
|
+
media_url: string | null;
|
|
560
565
|
message: string;
|
|
561
566
|
timestamp_observation: string;
|
|
562
567
|
}[];
|