mostly getting device and rooms correctly

dev
Anton Franzluebbers 2023-07-07 22:45:30 -04:00
parent c6670adec0
commit 2555c3082f
12 changed files with 387 additions and 96 deletions

View File

@ -8,6 +8,8 @@
"name": "example-dashboard", "name": "example-dashboard",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"humanize-duration": "^3.28.0",
"luxon": "^3.3.0",
"pocketbase": "^0.15.2" "pocketbase": "^0.15.2"
}, },
"devDependencies": { "devDependencies": {
@ -1802,6 +1804,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/humanize-duration": {
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.28.0.tgz",
"integrity": "sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A=="
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.2.4", "version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -2034,6 +2041,14 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/luxon": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz",
"integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==",
"engines": {
"node": ">=12"
}
},
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.1", "version": "0.30.1",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz",
@ -4383,6 +4398,11 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true "dev": true
}, },
"humanize-duration": {
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.28.0.tgz",
"integrity": "sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A=="
},
"ignore": { "ignore": {
"version": "5.2.4", "version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -4563,6 +4583,11 @@
"yallist": "^4.0.0" "yallist": "^4.0.0"
} }
}, },
"luxon": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz",
"integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg=="
},
"magic-string": { "magic-string": {
"version": "0.30.1", "version": "0.30.1",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz",

View File

@ -21,16 +21,18 @@
"eslint-plugin-svelte": "^2.30.0", "eslint-plugin-svelte": "^2.30.0",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1", "prettier-plugin-svelte": "^2.10.1",
"sass": "^1.63.6",
"svelte": "^4.0.0", "svelte": "^4.0.0",
"svelte-check": "^3.4.3", "svelte-check": "^3.4.3",
"svelte-preprocess": "^5.0.4", "svelte-preprocess": "^5.0.4",
"tslib": "^2.4.1", "tslib": "^2.4.1",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"vite": "^4.3.6", "vite": "^4.3.6"
"sass": "^1.63.6"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"humanize-duration": "^3.28.0",
"luxon": "^3.3.0",
"pocketbase": "^0.15.2" "pocketbase": "^0.15.2"
} }
} }

View File

@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/favicons/favicon.ico" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
%sveltekit.head% %sveltekit.head%
</head> </head>

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { currentUser, pb } from '../velconnect'; import { currentUser, pb } from '$lib/js/velconnect';
let email: string; let email: string;
let password: string; let password: string;

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { currentDevice, currentUser, pb, type Device, pairedDevices } from '../velconnect'; import { currentDevice, currentUser, pb, type DeviceData, pairedDevices } from '$lib/js/velconnect';
let pairingCode: string; let pairingCode: string;
let errorMessage: string | null; let errorMessage: string | null;
@ -9,7 +9,7 @@
try { try {
let device = (await pb let device = (await pb
.collection('Device') .collection('Device')
.getFirstListItem(`pairing_code="${pairingCode}"`)) as Device; .getFirstListItem(`pairing_code="${pairingCode}"`)) as DeviceData;
// add it to the local data // add it to the local data
currentDevice.set(device.id); currentDevice.set(device.id);

View File

@ -0,0 +1,30 @@
import { DateTime } from 'luxon';
import humanizeDuration from 'humanize-duration';
export function prettyDate(date: string | Date | DateTime, includeYear = false) {
if (date == null) return '';
let d: DateTime;
if (date instanceof Date) {
d = DateTime.fromJSDate(date);
} else if (date instanceof DateTime) {
d = date;
} else {
date = date.replace(' ', 'T');
d = DateTime.fromISO(date);
}
// return DateTime.fromISO(date).toFormat("yyyy-LL-dd hh:mm a ZZZZ");
const fromNow = DateTime.utc().minus(d.toMillis()).toMillis();
const yearReplace = includeYear ? '' : ', 2023';
if (fromNow > 0) {
return `${d.toLocaleString(DateTime.DATETIME_MED)} (${humanizeDuration(fromNow, {
round: true,
largest: 1
})} ago)`.replaceAll(yearReplace, '');
} else {
return `${d.toLocaleString(DateTime.DATETIME_MED)} (in ${humanizeDuration(fromNow, {
round: true,
largest: 1
})})`.replaceAll(yearReplace, '');
}
}

View File

@ -18,7 +18,7 @@ export const currentDevice = writable('');
interface HasData extends Record { interface HasData extends Record {
data: { [key: string]: string }; data: { [key: string]: string };
} }
export interface Device extends Record { export interface DeviceData extends Record {
current_room: string; current_room: string;
current_app: string; current_app: string;
data: { [key: string]: string }; data: { [key: string]: string };

View File

@ -2,23 +2,26 @@
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import { import {
currentDevice, currentDevice,
type Device, type DeviceData,
pairedDevices, pairedDevices,
pb, pb,
type RoomData, type RoomData,
currentUser currentUser
} from '../lib/velconnect'; } from '$lib/js/velconnect';
import Login from '$lib/components/Login.svelte'; import Login from '$lib/components/Login.svelte';
import Pair from '$lib/components/Pair.svelte'; import Pair from '$lib/components/Pair.svelte';
import { prettyDate } from '$lib/js/util';
if ($currentDevice == '' && $pairedDevices.length > 0) { if ($currentDevice == '' && $pairedDevices.length > 0) {
currentDevice.set($pairedDevices[0]); currentDevice.set($pairedDevices[0]);
} }
let unsubscribeDeviceData: () => void; let unsubscribeDeviceData: () => void;
let unsubscribeRoomData: () => void;
let unsubscribeCurrentDevice: () => void; let unsubscribeCurrentDevice: () => void;
let unsubscribeCurrentUser: () => void;
let deviceData: Device | null; let deviceData: DeviceData | null;
let roomData: RoomData | null; let roomData: RoomData | null;
let sending = false; let sending = false;
@ -38,21 +41,50 @@
unsubscribeDeviceData?.(); unsubscribeDeviceData?.();
if (val != '') { if (val != '') {
deviceData = await pb.collection('Device').getOne($currentDevice); deviceData = await pb.collection('Device').getOne($currentDevice);
unsubscribeDeviceData = await pb.collection('Device').subscribe(val, (data) => { if (deviceData != null) getRoomData(deviceData);
deviceData = data.record as Device; unsubscribeDeviceData = await pb.collection('Device').subscribe(val, async (data) => {
deviceData = data.record as DeviceData;
getRoomData(deviceData);
}); });
} else { } else {
deviceData = null; deviceData = null;
roomData = null; roomData = null;
} }
}); });
unsubscribeCurrentUser = currentUser.subscribe((user) => {
pairedDevices.set(user?.devices ?? []);
});
}); });
onDestroy(() => { onDestroy(() => {
unsubscribeCurrentDevice?.(); unsubscribeCurrentDevice?.();
unsubscribeDeviceData?.(); unsubscribeDeviceData?.();
unsubscribeRoomData?.();
unsubscribeCurrentUser?.();
}); });
async function getRoomData(deviceData: DeviceData) {
// get room data
unsubscribeRoomData?.();
// create or just fetch room by name
roomData = await fetch(
`${pb.baseUrl}/data_block/${deviceData.current_app}_${deviceData.current_room}`,
{
method: 'POST'
}
).then((r) => r.json());
console.log(roomData);
if (roomData) {
unsubscribeDeviceData = await pb.collection('DataBlock').subscribe(roomData.id, (data) => {
roomData = data.record as RoomData;
unsubscribeRoomData?.();
});
} else {
console.error('Failed to get or create room');
}
}
let abortController = new AbortController(); let abortController = new AbortController();
function delayedSend() { function delayedSend() {
console.log('fn: delayedSend()'); console.log('fn: delayedSend()');
@ -106,6 +138,10 @@
} }
</script> </script>
<svelte:head>
<title>VEL-Connect</title>
</svelte:head>
<h1>VEL-Connect</h1> <h1>VEL-Connect</h1>
<img src="/img/velconnect_logo_1.png" alt="logo" width="70px" height="28px" /> <img src="/img/velconnect_logo_1.png" alt="logo" width="70px" height="28px" />
<p> <p>
@ -119,20 +155,22 @@
<div> <div>
<h3>Devices:</h3> <h3>Devices:</h3>
{#each $pairedDevices as d} <div class="device-list">
<div> {#each $pairedDevices as d}
<button <div>
on:click={() => { <button
currentDevice.set(d); on:click={() => {
}}>{d}</button currentDevice.set(d);
> }}>{d}</button
<button >
on:click={() => { <button
removeDevice(d); on:click={() => {
}}>x</button removeDevice(d);
> }}>x</button
</div> >
{/each} </div>
{/each}
</div>
{#if $pairedDevices.length == 0} {#if $pairedDevices.length == 0}
<p>No devices paired. Enter a pairing code above.</p> <p>No devices paired. Enter a pairing code above.</p>
{/if} {/if}
@ -158,11 +196,11 @@
</device-field> </device-field>
<device-field> <device-field>
<h6>First Seen</h6> <h6>First Seen</h6>
<p>{deviceData.created}</p> <p>{prettyDate(deviceData.created)}</p>
</device-field> </device-field>
<device-field> <device-field>
<h6>Last Seen</h6> <h6>Last Seen</h6>
<p>{deviceData.updated}</p> <p>{prettyDate(deviceData.updated)}</p>
</device-field> </device-field>
</div> </div>
@ -218,8 +256,11 @@
</device-field> </device-field>
</div> </div>
<h6>Raw JSON:</h6> <h3>Raw JSON:</h3>
<h6>Device Data</h6>
<pre><code>{JSON.stringify(deviceData, null, 2)}</code></pre> <pre><code>{JSON.stringify(deviceData, null, 2)}</code></pre>
<h6>Room Data</h6>
<pre><code>{JSON.stringify(roomData, null, 2)}</code></pre>
{/if} {/if}
<style lang="scss"> <style lang="scss">
@ -232,4 +273,19 @@
margin: 0; margin: 0;
} }
} }
.device-list {
display: flex;
flex-direction: column;
width: fit-content;
gap: 0.5em;
& > div {
display: flex;
gap: 0.2em;
& > button:first-child {
flex-grow: 1;
}
}
}
</style> </style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -35,17 +36,17 @@ namespace VELConnect
public class Device public class Device
{ {
public readonly string id; [CanBeNull] public readonly string id;
public readonly DateTime created; [CanBeNull] public string created = null;
public readonly DateTime updated; [CanBeNull] public string updated = null;
public string device_id; [CanBeNull] public string device_id;
public string os_info; [CanBeNull] public string os_info;
public string friendly_name; [CanBeNull] public string friendly_name;
public string modified_by; [CanBeNull] public string modified_by;
public string current_app; [CanBeNull] public string current_app;
public string current_room; [CanBeNull] public string current_room;
public string pairing_code; [CanBeNull] public string pairing_code;
public DateTime last_online; [CanBeNull] public string last_online;
public Dictionary<string, string> data; public Dictionary<string, string> data;
/// <summary> /// <summary>
@ -88,9 +89,9 @@ namespace VELConnect
public class UserCount public class UserCount
{ {
public readonly string id; [CanBeNull] public readonly string id;
public readonly DateTime created; public readonly DateTime? created;
public readonly DateTime updated; public readonly DateTime? updated;
public string device_id; public string device_id;
public string app_id; public string app_id;
public string room_id; public string room_id;
@ -100,7 +101,20 @@ namespace VELConnect
public string platform; public string platform;
} }
public enum DeviceField
{
device_id,
os_info,
friendly_name,
modified_by,
current_app,
current_room,
pairing_code,
last_online
}
public State lastState; public State lastState;
public State state;
public static Action<State> OnInitialState; public static Action<State> OnInitialState;
public static Action<string, string> OnDeviceFieldChanged; public static Action<string, string> OnDeviceFieldChanged;
@ -136,7 +150,7 @@ namespace VELConnect
get get
{ {
Hash128 hash = new Hash128(); Hash128 hash = new Hash128();
hash.Append(DeviceId); hash.Append(deviceId);
// change once a day // change once a day
hash.Append(DateTime.UtcNow.DayOfYear); hash.Append(DateTime.UtcNow.DayOfYear);
// between 1000 and 9999 inclusive (any 4 digit number) // between 1000 and 9999 inclusive (any 4 digit number)
@ -144,52 +158,52 @@ namespace VELConnect
} }
} }
private static string DeviceId private static string deviceId;
{
get
{
#if UNITY_EDITOR
// allows running multiple builds on the same computer
// return SystemInfo.deviceUniqueIdentifier + Hash128.Compute(Application.dataPath);
return SystemInfo.deviceUniqueIdentifier + "_EDITOR";
#else
return SystemInfo.deviceUniqueIdentifier;
#endif
}
}
private void Awake() private void Awake()
{ {
if (_instance != null) Debug.LogError("VELConnectManager instance already exists", this); if (_instance != null) Debug.LogError("VELConnectManager instance already exists", this);
_instance = this; _instance = this;
// Compute device id
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
StringBuilder sb = new StringBuilder(SystemInfo.deviceUniqueIdentifier);
sb.Append(Application.productName);
#if UNITY_EDITOR
// allows running multiple builds on the same computer
// return SystemInfo.deviceUniqueIdentifier + Hash128.Compute(Application.dataPath);
sb.Append(Application.dataPath);
sb.Append("EDITOR");
#endif
string id = Convert.ToBase64String(md5.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString())));
deviceId = id[..15];
} }
// Start is called before the first frame update // Start is called before the first frame update
private void Start() private void Start()
{ {
SetDeviceField(new State.Device SetDeviceField(new Dictionary<DeviceField, string>
{ {
os_info = SystemInfo.operatingSystem, { DeviceField.os_info, SystemInfo.operatingSystem },
friendly_name = SystemInfo.deviceName, { DeviceField.friendly_name, SystemInfo.deviceName },
current_app = Application.productName, { DeviceField.current_app, Application.productName },
pairing_code = PairingCode, { DeviceField.pairing_code, PairingCode },
}); });
UpdateUserCount(); // UpdateUserCount();
StartCoroutine(SlowLoop()); StartCoroutine(SlowLoop());
VelNetManager.OnJoinedRoom += room => VelNetManager.OnJoinedRoom += room =>
{ {
SetDeviceField(new State.Device SetDeviceField(new Dictionary<DeviceField, string>
{ {
current_app = Application.productName, { DeviceField.current_app, Application.productName },
current_room = room, { DeviceField.current_room, room },
}); });
}; };
} }
private void UpdateUserCount(bool leaving = false) private void UpdateUserCount(bool leaving = false)
{ {
if (!VelNetManager.InRoom) return; if (!VelNetManager.InRoom) return;
@ -198,7 +212,7 @@ namespace VELConnect
{ {
UserCount postData = new UserCount UserCount postData = new UserCount
{ {
device_id = DeviceId, device_id = deviceId,
app_id = Application.productName, app_id = Application.productName,
room_id = VelNetManager.Room ?? "", room_id = VelNetManager.Room ?? "",
total_users = rooms.rooms.Sum(r => r.numUsers) - (leaving ? 1 : 0), total_users = rooms.rooms.Sum(r => r.numUsers) - (leaving ? 1 : 0),
@ -219,9 +233,9 @@ namespace VELConnect
{ {
try try
{ {
GetRequestCallback(velConnectUrl + "/state/device/" + DeviceId, json => GetRequestCallback(velConnectUrl + "/state/device/" + deviceId, json =>
{ {
State state = JsonConvert.DeserializeObject<State>(json); state = JsonConvert.DeserializeObject<State>(json);
if (state == null) return; if (state == null) return;
bool isInitialState = false; bool isInitialState = false;
@ -380,6 +394,10 @@ namespace VELConnect
} }
lastState = state; lastState = state;
if (lastState?.device?.pairing_code == null)
{
Debug.LogError("Pairing code nulllll");
}
}); });
} }
catch (Exception e) catch (Exception e)
@ -510,33 +528,46 @@ namespace VELConnect
return _instance != null ? _instance.lastState?.room?.TryGetData(key) : null; return _instance != null ? _instance.lastState?.room?.TryGetData(key) : null;
} }
/// <summary> /// <summary>
/// Sets data on the device keys themselves /// Sets data on the device keys themselves
/// These are fixed fields defined for every application /// These are fixed fields defined for every application
/// </summary> /// </summary>
public static void SetDeviceField(State.Device device) public static void SetDeviceField(Dictionary<DeviceField, string> device)
{ {
device.last_online = DateTime.UtcNow; device[DeviceField.last_online] = DateTime.UtcNow.ToLongDateString();
// update our local state, so we don't get change events on our own updates if (_instance.state?.device != null)
if (_instance.lastState?.device != null)
{ {
FieldInfo[] fields = device.GetType().GetFields();
// loop through all the fields in the device // loop through all the fields in the device
foreach (FieldInfo fieldInfo in fields) foreach (DeviceField key in device.Keys.ToArray())
{ {
fieldInfo.SetValue(_instance.lastState.device, fieldInfo.GetValue(device)); FieldInfo field = _instance.state.device.GetType().GetField(key.ToString());
if ((string)field.GetValue(_instance.state.device) != device[key])
{
if (_instance.lastState?.device != null)
{
// update our local state, so we don't get change events on our own updates
field.SetValue(_instance.lastState.device, device[key]);
}
}
else
{
// don't send this field, since it's the same
device.Remove(key);
}
}
// last_online field always changes
if (device.Keys.Count <= 1)
{
// nothing changed, don't send
return;
} }
} }
PostRequestCallback( PostRequestCallback(
_instance.velConnectUrl + "/device/" + DeviceId, _instance.velConnectUrl + "/device/" + deviceId,
JsonConvert.SerializeObject(device, Formatting.None, new JsonSerializerSettings JsonConvert.SerializeObject(device)
{
NullValueHandling = NullValueHandling.Ignore
})
); );
} }
@ -545,23 +576,44 @@ namespace VELConnect
/// </summary> /// </summary>
public static void SetDeviceData(Dictionary<string, string> data) public static void SetDeviceData(Dictionary<string, string> data)
{ {
State.Device device = new State.Device if (_instance.state?.device != null)
{ {
last_online = DateTime.UtcNow, foreach (string key in data.Keys.ToList())
data = data,
};
// update our local state, so we don't get change events on our own updates
if (_instance.lastState?.device != null)
{
foreach (KeyValuePair<string, string> kvp in data)
{ {
_instance.lastState.device.data[kvp.Key] = kvp.Value; // if the value is unchanged from the current state, remove it so we don't double-update
if (_instance.state.device.data.TryGetValue(key, out string val) && val == data[key])
{
data.Remove(key);
}
else
{
// update our local state, so we don't get change events on our own updates
if (_instance.lastState?.device?.data != null)
{
_instance.lastState.device.data[key] = data[key];
}
}
} }
// nothing was changed
if (data.Keys.Count == 0)
{
return;
}
// if we have no data, just set the whole thing
if (_instance.lastState?.device != null) _instance.lastState.device.data ??= data;
} }
Dictionary<string, object> device = new Dictionary<string, object>
{
{ "last_online", DateTime.UtcNow.ToLongDateString() },
{ "data", data },
};
PostRequestCallback( PostRequestCallback(
_instance.velConnectUrl + "/device/" + DeviceId, _instance.velConnectUrl + "/device/" + deviceId,
JsonConvert.SerializeObject(device, Formatting.None, new JsonSerializerSettings JsonConvert.SerializeObject(device, Formatting.None, new JsonSerializerSettings
{ {
NullValueHandling = NullValueHandling.Ignore NullValueHandling = NullValueHandling.Ignore
@ -589,6 +641,24 @@ namespace VELConnect
data = data data = data
}; };
// remove keys that already match our current state
if (_instance.state?.room != null)
{
foreach (string key in data.Keys.ToArray())
{
if (_instance.state.room.data[key] == data[key])
{
data.Remove(key);
}
}
}
// if we have no changed values
if (data.Keys.Count == 0)
{
return;
}
// update our local state, so we don't get change events on our own updates // update our local state, so we don't get change events on our own updates
if (_instance.lastState?.room != null) if (_instance.lastState?.room != null)
{ {
@ -727,7 +797,7 @@ namespace VELConnect
private void OnApplicationFocus(bool focus) private void OnApplicationFocus(bool focus)
{ {
UpdateUserCount(!focus); // UpdateUserCount(!focus);
} }
} }
} }

View File

@ -0,0 +1,54 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
// add
new_current_room_id := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "wvpaovjo",
"name": "current_room_id",
"type": "relation",
"required": false,
"unique": false,
"options": {
"collectionId": "3qwwkz4wb0lyi78",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": []
}
}`), new_current_room_id)
collection.Schema.AddField(new_current_room_id)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
// remove
collection.Schema.RemoveField("wvpaovjo")
return dao.SaveCollection(collection)
})
}

View File

@ -0,0 +1,54 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
// remove
collection.Schema.RemoveField("wvpaovjo")
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
// add
del_current_room_id := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "wvpaovjo",
"name": "current_room_id",
"type": "relation",
"required": false,
"unique": false,
"options": {
"collectionId": "3qwwkz4wb0lyi78",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": []
}
}`), del_current_room_id)
collection.Schema.AddField(del_current_room_id)
return dao.SaveCollection(collection)
})
}