v4 hopefully no breaky by pushy
parent
8da2f1b4d6
commit
2e553732dc
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||
ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts
|
||||
- name: connect and pull
|
||||
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd /home/ubuntu/VEL-Connect-v3/velconnect && git pull && docker compose up -d --build && exit"
|
||||
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd /home/ubuntu/VEL-Connect-v4/velconnect && git pull && docker compose -p velconnect-v4 up -d --build && exit"
|
||||
- name: cleanup
|
||||
run: rm -rf ~/.ssh
|
||||
|
||||
|
|
|
|||
|
|
@ -281,35 +281,38 @@ namespace VELConnect
|
|||
: null;
|
||||
|
||||
DeviceField fieldName;
|
||||
if (Enum.TryParse(fieldInfo.Name, out fieldName) && newValue != oldValue)
|
||||
if (Enum.TryParse(fieldInfo.Name, out fieldName))
|
||||
{
|
||||
try
|
||||
if (newValue != oldValue)
|
||||
{
|
||||
if (!isInitialState) OnDeviceFieldChanged?.Invoke(fieldName, newValue);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
|
||||
// send specific listeners data
|
||||
if (deviceFieldCallbacks.ContainsKey(fieldName))
|
||||
{
|
||||
// clear the list of old listeners
|
||||
deviceFieldCallbacks[fieldName].RemoveAll(e => e.keepAliveObject == null);
|
||||
|
||||
// send the callbacks
|
||||
foreach (CallbackListener e in deviceFieldCallbacks[fieldName])
|
||||
try
|
||||
{
|
||||
if (!isInitialState || e.sendInitialState)
|
||||
if (!isInitialState) OnDeviceFieldChanged?.Invoke(fieldName, newValue);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
|
||||
// send specific listeners data
|
||||
if (deviceFieldCallbacks.ContainsKey(fieldName))
|
||||
{
|
||||
// clear the list of old listeners
|
||||
deviceFieldCallbacks[fieldName].RemoveAll(e => e.keepAliveObject == null);
|
||||
|
||||
// send the callbacks
|
||||
foreach (CallbackListener e in deviceFieldCallbacks[fieldName])
|
||||
{
|
||||
try
|
||||
if (!isInitialState || e.sendInitialState)
|
||||
{
|
||||
e.callback(newValue);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex);
|
||||
try
|
||||
{
|
||||
e.callback(newValue);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "edu.uga.engr.vel.vel-connect",
|
||||
"displayName": "VEL-Connect",
|
||||
"version": "2.1.3",
|
||||
"version": "4.0.0",
|
||||
"unity": "2019.1",
|
||||
"description": "Web-based configuration for VR applications",
|
||||
"keywords": [],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
dist/
|
||||
lib/
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"name": "@velaboratory/velconnect",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@velaboratory/velconnect",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pocketbase": "^0.15.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/pocketbase": {
|
||||
"version": "0.15.3",
|
||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.15.3.tgz",
|
||||
"integrity": "sha512-sjM0XO4wHUlVZs94VhRJi4FeYtbLqvxFbRDJlfjFb/4FkxypbGwxLM4HDAEr8q6jdreuxAM1/n/b5HB1GjQ1Vg=="
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
|
||||
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"pocketbase": {
|
||||
"version": "0.15.3",
|
||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.15.3.tgz",
|
||||
"integrity": "sha512-sjM0XO4wHUlVZs94VhRJi4FeYtbLqvxFbRDJlfjFb/4FkxypbGwxLM4HDAEr8q6jdreuxAM1/n/b5HB1GjQ1Vg=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
|
||||
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "@velaboratory/velconnect",
|
||||
"version": "1.0.0",
|
||||
"description": "Use VEL-Connect with a dashboard",
|
||||
"main": "src/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc --module commonjs"
|
||||
},
|
||||
"author": "VEL",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pocketbase": "^0.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
import PocketBase from "pocketbase";
|
||||
import type { Record } from "pocketbase";
|
||||
|
||||
|
||||
export interface Device extends Record {
|
||||
os_info: string;
|
||||
friendly_name: string;
|
||||
current_room: string;
|
||||
current_app: string;
|
||||
pairing_code: string;
|
||||
data: string;
|
||||
expand: { data?: DataBlock };
|
||||
}
|
||||
export interface DataBlock extends Record {
|
||||
block_id: string;
|
||||
owner: string;
|
||||
data: { [key: string]: string };
|
||||
}
|
||||
|
||||
export class VELConnect {
|
||||
|
||||
pb: PocketBase;
|
||||
debugLog = false;
|
||||
|
||||
constructor() {
|
||||
this.pb = new PocketBase();
|
||||
|
||||
this.pb.authStore.onChange((auth) => {
|
||||
console.log("authStore changed", auth);
|
||||
currentUser.set(pb.authStore.model);
|
||||
if (pb.authStore.isValid) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export const currentUser = writable(pb.authStore.model);
|
||||
|
||||
|
||||
|
||||
export const pairedDevices = writable<string[]>([]);
|
||||
export const currentDeviceId = writable("");
|
||||
|
||||
|
||||
// const device = get(currentDevice);
|
||||
// if (device == '' && device.length > 0) {
|
||||
// currentDevice.set(device[0]);
|
||||
// }
|
||||
|
||||
let unsubscribeDeviceFields: () => void;
|
||||
let unsubscribeDeviceData: () => void;
|
||||
let unsubscribeRoomData: () => void;
|
||||
let unsubscribeCurrentDevice: () => void;
|
||||
let unsubscribeCurrentUser: () => void;
|
||||
|
||||
export let deviceFields = writable<Device | null>(null);
|
||||
export let deviceData = writable<DataBlock | null>(null);
|
||||
export let roomData = writable<DataBlock | null>(null);
|
||||
|
||||
export let sending = false;
|
||||
|
||||
export async function startListening(baseUrl: string) {
|
||||
pb.baseUrl = baseUrl;
|
||||
if (get(currentDeviceId) != "") {
|
||||
const d = (await pb.collection("Device").getOne(get(currentDeviceId), {
|
||||
expand: "data",
|
||||
})) as Device;
|
||||
deviceData.set(d.expand.data as DataBlock);
|
||||
// we don't need expand anymore, since it doesn't work in subscribe()
|
||||
d.expand = {};
|
||||
deviceFields.set(d);
|
||||
}
|
||||
|
||||
unsubscribeCurrentDevice = currentDeviceId.subscribe(async (val) => {
|
||||
log("currentDeviceId subscribe change event");
|
||||
unsubscribeDeviceFields?.();
|
||||
unsubscribeDeviceData?.();
|
||||
if (val != "") {
|
||||
const d = (await pb
|
||||
.collection("Device")
|
||||
.getOne(get(currentDeviceId), { expand: "data" })) as Device;
|
||||
deviceData.set(d.expand.data as DataBlock);
|
||||
// we don't need expand anymore, since it doesn't work in subscribe()
|
||||
d.expand = {};
|
||||
deviceFields.set(d);
|
||||
|
||||
unsubscribeDeviceData = await pb
|
||||
.collection("DataBlock")
|
||||
.subscribe(d.data, async (data) => {
|
||||
log("deviceData subscribe change event");
|
||||
deviceData.set(data.record as DataBlock);
|
||||
});
|
||||
|
||||
unsubscribeDeviceFields = await pb
|
||||
.collection("Device")
|
||||
.subscribe(val, async (data) => {
|
||||
log("deviceFields subscribe change event");
|
||||
const d = data.record as Device;
|
||||
deviceFields.set(d);
|
||||
|
||||
// if the devie changes, the devicedata could change, so we need to resubscribe
|
||||
unsubscribeDeviceData?.();
|
||||
unsubscribeDeviceData = await pb
|
||||
.collection("DataBlock")
|
||||
.subscribe(d.data, async (data) => {
|
||||
log("deviceData subscribe change event");
|
||||
deviceData.set(data.record as DataBlock);
|
||||
});
|
||||
|
||||
getRoomData(d);
|
||||
});
|
||||
|
||||
if (d != null) getRoomData(d);
|
||||
} else {
|
||||
deviceFields.set(null);
|
||||
deviceData.set(null);
|
||||
roomData.set(null);
|
||||
}
|
||||
});
|
||||
|
||||
unsubscribeCurrentUser = currentUser.subscribe((user) => {
|
||||
log(`currentUser changed ${user}`);
|
||||
pairedDevices.set(user?.["devices"] ?? []);
|
||||
currentDeviceId.set(get(pairedDevices)[0] ?? "");
|
||||
});
|
||||
}
|
||||
|
||||
export function stopListening() {
|
||||
unsubscribeCurrentDevice?.();
|
||||
unsubscribeDeviceFields?.();
|
||||
unsubscribeDeviceData?.();
|
||||
unsubscribeRoomData?.();
|
||||
unsubscribeCurrentUser?.();
|
||||
console.log("Stop listening");
|
||||
}
|
||||
|
||||
async function getRoomData(device: Device) {
|
||||
unsubscribeRoomData?.();
|
||||
|
||||
// create or just fetch room by name
|
||||
let r: DataBlock | null = null;
|
||||
try {
|
||||
r = (await pb
|
||||
.collection("DataBlock")
|
||||
.getFirstListItem(
|
||||
`block_id="${device.current_app}_${device.current_room}"`
|
||||
)) as DataBlock;
|
||||
} catch (e: any) {
|
||||
r = (await pb.collection("DataBlock").create({
|
||||
block_id: `${device.current_app}_${device.current_room}`,
|
||||
category: "room",
|
||||
data: {},
|
||||
})) as DataBlock;
|
||||
}
|
||||
roomData.set(r);
|
||||
if (r != null) {
|
||||
unsubscribeRoomData = await pb
|
||||
.collection("DataBlock")
|
||||
.subscribe(r.id, (data) => {
|
||||
log("roomData subscribe change event");
|
||||
roomData.set(data.record as DataBlock);
|
||||
});
|
||||
} else {
|
||||
console.error("Failed to get or create room");
|
||||
}
|
||||
}
|
||||
|
||||
let abortController = new AbortController();
|
||||
export function delayedSend() {
|
||||
console.log("fn: delayedSend()");
|
||||
|
||||
// abort the previous send
|
||||
abortController.abort();
|
||||
const newAbortController = new AbortController();
|
||||
abortController = newAbortController;
|
||||
setTimeout(() => {
|
||||
if (!newAbortController.signal.aborted) {
|
||||
send();
|
||||
} else {
|
||||
console.log("aborted");
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
export function send() {
|
||||
console.log("sending...");
|
||||
sending = true;
|
||||
let promises: Promise<any>[] = [];
|
||||
const device = get(deviceFields);
|
||||
const data = get(deviceData);
|
||||
const room = get(roomData);
|
||||
// TODO send changes only
|
||||
if (device) {
|
||||
promises.push(pb.collection("Device").update(device.id, device));
|
||||
}
|
||||
if (data) {
|
||||
promises.push(pb.collection("DataBlock").update(data.id, data));
|
||||
}
|
||||
if (room) {
|
||||
promises.push(pb.collection("DataBlock").update(room.id, room));
|
||||
}
|
||||
Promise.all(promises).then(() => {
|
||||
sending = false;
|
||||
});
|
||||
}
|
||||
|
||||
export function removeDevice(d: string) {
|
||||
pairedDevices.set(get(pairedDevices).filter((i) => i != d));
|
||||
|
||||
if (get(currentDeviceId) == d) {
|
||||
console.log("Removed current device");
|
||||
|
||||
// if there are still devices left
|
||||
if (get(pairedDevices).length > 0) {
|
||||
currentDeviceId.set(get(pairedDevices)[0] ?? "");
|
||||
} else {
|
||||
currentDeviceId.set("");
|
||||
}
|
||||
}
|
||||
|
||||
const user = get(currentUser);
|
||||
if (user) {
|
||||
user["devices"] = user["devices"].filter((i: string) => i != d);
|
||||
pb.collection("Users").update(user.id, user);
|
||||
}
|
||||
}
|
||||
|
||||
async function pair(pairingCode: string) {
|
||||
try {
|
||||
// find the device by pairing code
|
||||
const device = (await pb
|
||||
.collection("Device")
|
||||
.getFirstListItem(`pairing_code="${pairingCode}"`)) as Device;
|
||||
|
||||
// add it to the local data
|
||||
currentDeviceId.set(device.id);
|
||||
if (!get(pairedDevices).includes(device.id)) {
|
||||
pairedDevices.set([...get(pairedDevices), device.id]);
|
||||
}
|
||||
|
||||
// add it to my account if logged in
|
||||
const u = get(currentUser);
|
||||
if (u) {
|
||||
// add the device to the user's devices
|
||||
u["devices"].push(device.id);
|
||||
|
||||
// add the account data to the device
|
||||
if (
|
||||
u.user_data == null ||
|
||||
u.user_data == undefined ||
|
||||
u.user_data == ""
|
||||
) {
|
||||
// create a new user data block if it doesn't exist on the user already
|
||||
const userDataBlock = await pb.collection("DataBlock").create({
|
||||
category: "device",
|
||||
data: {},
|
||||
owner: u.id,
|
||||
});
|
||||
u.user_data = userDataBlock.id;
|
||||
}
|
||||
device["data"] = u.user_data;
|
||||
device["owner"] = u.id;
|
||||
device["past_owners"] = [...device["past_owners"], u.id];
|
||||
|
||||
await pb.collection("Device").update(device.id, device);
|
||||
await pb.collection("Users").update(u.id, u);
|
||||
}
|
||||
|
||||
return { error: null };
|
||||
} catch (e) {
|
||||
console.error("Not found: " + e);
|
||||
if (e == "ClientResponseError 404: The requested resource wasn't found.") {
|
||||
return {
|
||||
error: "Device not found with this pairing code.",
|
||||
};
|
||||
}
|
||||
return {
|
||||
error: e as string,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function login(username: string, password: string) {
|
||||
try {
|
||||
await pb.collection("Users").authWithPassword(username, password);
|
||||
return {};
|
||||
} catch (err: any) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function signUp(username: string, password: string) {
|
||||
try {
|
||||
const data = {
|
||||
username: username,
|
||||
password,
|
||||
passwordConfirm: password,
|
||||
};
|
||||
await pb.collection("Users").create(data);
|
||||
return await login(username, password);
|
||||
} catch (err: any) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
export function signOut() {
|
||||
pb.authStore.clear();
|
||||
}
|
||||
|
||||
function log(msg: string) {
|
||||
if (debugLog) {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"include": ["src"],
|
||||
"compilerOptions": {
|
||||
"target": "es2018",
|
||||
"moduleResolution": "node", // don't have to import actual filenames, can import extensionless files
|
||||
"declaration": true, // generate .d.ts files
|
||||
"sourceMap": true, // generate source map
|
||||
"outDir": "dist", // output compiled js, d.ts, and source map to dist folder
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@velaboratory/velconnect-svelte",
|
||||
"version": "1.0.6",
|
||||
"version": "4.0.0",
|
||||
"description": "Use VEL-Connect with a Svelte dashboard",
|
||||
"main": "src/index.js",
|
||||
"files": [
|
||||
|
|
@ -13,11 +13,11 @@
|
|||
"author": "VEL",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pocketbase": "^0.15.3",
|
||||
"svelte": "^4.0.5",
|
||||
"pocketbase": "^0.19.0",
|
||||
"svelte": "^4.2.8",
|
||||
"@velaboratory/velconnect": "../velconnect-npm"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.1.6"
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
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("_pb_users_auth_")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update
|
||||
edit_user_data := &schema.SchemaField{}
|
||||
json.Unmarshal([]byte(`{
|
||||
"system": false,
|
||||
"id": "xvw8arlm",
|
||||
"name": "user_data",
|
||||
"type": "relation",
|
||||
"required": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"collectionId": "3qwwkz4wb0lyi78",
|
||||
"cascadeDelete": false,
|
||||
"minSelect": null,
|
||||
"maxSelect": null,
|
||||
"displayFields": [
|
||||
"data"
|
||||
]
|
||||
}
|
||||
}`), edit_user_data)
|
||||
collection.Schema.AddField(edit_user_data)
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
}, func(db dbx.Builder) error {
|
||||
dao := daos.New(db);
|
||||
|
||||
collection, err := dao.FindCollectionByNameOrId("_pb_users_auth_")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update
|
||||
edit_user_data := &schema.SchemaField{}
|
||||
json.Unmarshal([]byte(`{
|
||||
"system": false,
|
||||
"id": "xvw8arlm",
|
||||
"name": "user_data",
|
||||
"type": "relation",
|
||||
"required": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"collectionId": "3qwwkz4wb0lyi78",
|
||||
"cascadeDelete": false,
|
||||
"minSelect": null,
|
||||
"maxSelect": 1,
|
||||
"displayFields": [
|
||||
"data"
|
||||
]
|
||||
}
|
||||
}`), edit_user_data)
|
||||
collection.Schema.AddField(edit_user_data)
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
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("_pb_users_auth_")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update
|
||||
edit_profiles := &schema.SchemaField{}
|
||||
json.Unmarshal([]byte(`{
|
||||
"system": false,
|
||||
"id": "xvw8arlm",
|
||||
"name": "profiles",
|
||||
"type": "relation",
|
||||
"required": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"collectionId": "3qwwkz4wb0lyi78",
|
||||
"cascadeDelete": false,
|
||||
"minSelect": null,
|
||||
"maxSelect": null,
|
||||
"displayFields": [
|
||||
"data"
|
||||
]
|
||||
}
|
||||
}`), edit_profiles)
|
||||
collection.Schema.AddField(edit_profiles)
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
}, func(db dbx.Builder) error {
|
||||
dao := daos.New(db);
|
||||
|
||||
collection, err := dao.FindCollectionByNameOrId("_pb_users_auth_")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update
|
||||
edit_profiles := &schema.SchemaField{}
|
||||
json.Unmarshal([]byte(`{
|
||||
"system": false,
|
||||
"id": "xvw8arlm",
|
||||
"name": "user_data",
|
||||
"type": "relation",
|
||||
"required": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"collectionId": "3qwwkz4wb0lyi78",
|
||||
"cascadeDelete": false,
|
||||
"minSelect": null,
|
||||
"maxSelect": null,
|
||||
"displayFields": [
|
||||
"data"
|
||||
]
|
||||
}
|
||||
}`), edit_profiles)
|
||||
collection.Schema.AddField(edit_profiles)
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue