add /inventory page
This commit is contained in:
parent
9770ca861a
commit
8716d3f692
8 changed files with 329 additions and 5 deletions
108
deploy/dev/instance_a/testdata.py
Normal file
108
deploy/dev/instance_a/testdata.py
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import base64
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_data():
|
||||||
|
print('Creating test data for instance A')
|
||||||
|
|
||||||
|
from authentication.models import ToolshedUser
|
||||||
|
admin = None
|
||||||
|
try:
|
||||||
|
if ToolshedUser.objects.filter(username='admin').exists():
|
||||||
|
admin = ToolshedUser.objects.get(username='admin')
|
||||||
|
else:
|
||||||
|
admin = ToolshedUser.objects.create_superuser('admin', 'admin@localhost', '')
|
||||||
|
admin.set_password('j7Th5TfCGUZmQ7U')
|
||||||
|
admin.save()
|
||||||
|
print('Created {}@{} with private key {} and public key {}'.format(admin.username, admin.domain,
|
||||||
|
admin.private_key,
|
||||||
|
admin.public_identity.public_key))
|
||||||
|
except Exception as e:
|
||||||
|
print('Admin user already exists, skipping')
|
||||||
|
|
||||||
|
from hostadmin.models import Domain
|
||||||
|
try:
|
||||||
|
domain1 = Domain.objects.create(name='localhost', owner=admin, open_registration=False)
|
||||||
|
domain1.save()
|
||||||
|
print('Created domain {} with closed registration'.format(domain1.name))
|
||||||
|
except Exception as e:
|
||||||
|
print('Domain localhost already exists, skipping')
|
||||||
|
|
||||||
|
domain2 = None
|
||||||
|
try:
|
||||||
|
if Domain.objects.filter(name='a.localhost').exists():
|
||||||
|
domain2 = Domain.objects.get(name='a.localhost')
|
||||||
|
domain2 = Domain.objects.create(name='a.localhost', owner=admin, open_registration=True)
|
||||||
|
domain2.save()
|
||||||
|
print('Created domain {} with open registration'.format(domain2.name))
|
||||||
|
except Exception as e:
|
||||||
|
print('Domain b.localhost already exists, skipping')
|
||||||
|
|
||||||
|
user = None
|
||||||
|
try:
|
||||||
|
if ToolshedUser.objects.filter(username='test_a').exists():
|
||||||
|
user = ToolshedUser.objects.get(username='test_a')
|
||||||
|
user = ToolshedUser.objects.create_user('test_a', 'testa@example.com', '', domain=domain2.name,
|
||||||
|
private_key='4ab79601edc400fd0263c7d5fd045ea7f5de28f1dd8905e2479f96893f3257d8')
|
||||||
|
user.set_password('test_a')
|
||||||
|
user.save()
|
||||||
|
print('Created user {}@{} with private key {} and public key {}'.format(user.username, user.domain,
|
||||||
|
user.private_key,
|
||||||
|
user.public_identity.public_key))
|
||||||
|
except Exception as e:
|
||||||
|
print('User foobar already exists, skipping')
|
||||||
|
|
||||||
|
from authentication.models import KnownIdentity
|
||||||
|
identity = None
|
||||||
|
try:
|
||||||
|
if KnownIdentity.objects.filter(username='test_b').exists():
|
||||||
|
identity = KnownIdentity.objects.get(username='test_b')
|
||||||
|
identity = KnownIdentity.objects.create(username='test_b', domain='b.localhost',
|
||||||
|
public_key='4b0dffe21764c591762615ef84cfea7fd4055fab3e9f58ae077fd8c79c34af91')
|
||||||
|
# pk '2ec1e7d5f5b8d5f87233944970d57f942095fe9e6c4fc49edde61fcd3fb1bf40'
|
||||||
|
identity.save()
|
||||||
|
print('Created identity {}@{} with public key {}'.format(identity.username, identity.domain,
|
||||||
|
identity.public_key))
|
||||||
|
except Exception as e:
|
||||||
|
print('Identity already exists, skipping')
|
||||||
|
|
||||||
|
from toolshed.models import Category
|
||||||
|
category1 = None
|
||||||
|
try:
|
||||||
|
if Category.objects.filter(name='Test Category').exists():
|
||||||
|
category1 = Category.objects.get(name='Test Category')
|
||||||
|
category1 = Category.objects.get_or_create(name='Test Category', origin='test')[0]
|
||||||
|
except Exception as e:
|
||||||
|
print('Category already exists, skipping')
|
||||||
|
|
||||||
|
from toolshed.models import InventoryItem
|
||||||
|
item1 = None
|
||||||
|
try:
|
||||||
|
if InventoryItem.objects.filter(name='Test Item').exists():
|
||||||
|
item1 = InventoryItem.objects.get(name='Test Item')
|
||||||
|
item1 = InventoryItem.objects.create(name='Test Item', description='This is a test item', category=category1,
|
||||||
|
availability_policy='rent', owned_quantity=1, owner=user)
|
||||||
|
item1.save()
|
||||||
|
except Exception as e:
|
||||||
|
print('Item already exists, skipping')
|
||||||
|
|
||||||
|
try:
|
||||||
|
item2 = InventoryItem.objects.create(name='Test Item 2', description='This is a test item', category=category1,
|
||||||
|
availability_policy='share', owned_quantity=1, owner=user)
|
||||||
|
item2.save()
|
||||||
|
print('Created test items with IDs "{}" and "{}"'.format(item1.id, item2.id))
|
||||||
|
except Exception as e:
|
||||||
|
print('Item already exists, skipping')
|
||||||
|
|
||||||
|
try:
|
||||||
|
user.friends.add(identity)
|
||||||
|
print('Added identity {} to friends list of user {}'.format(identity.username, user.username))
|
||||||
|
except Exception as e:
|
||||||
|
print('Identity already in friends list, skipping')
|
||||||
|
|
||||||
|
from files.models import File
|
||||||
|
try:
|
||||||
|
file1 = File.objects.create(mime_type='text/plain', data=base64.b64encode(b'testcontent1').decode('utf-8'))
|
||||||
|
file1.save()
|
||||||
|
print(f'Created file at /{file1.hash[:2]}/{file1.hash[2:4]}/{file1.hash[4:6]}/{file1.hash[6:]}')
|
||||||
|
except Exception as e:
|
||||||
|
print('File already exists, skipping')
|
108
deploy/dev/instance_b/testdata.py
Normal file
108
deploy/dev/instance_b/testdata.py
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import base64
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_data():
|
||||||
|
print('Creating test data for instance B')
|
||||||
|
|
||||||
|
from authentication.models import ToolshedUser
|
||||||
|
admin = None
|
||||||
|
try:
|
||||||
|
if ToolshedUser.objects.filter(username='admin').exists():
|
||||||
|
admin = ToolshedUser.objects.get(username='admin')
|
||||||
|
else:
|
||||||
|
admin = ToolshedUser.objects.create_superuser('admin', 'admin@localhost', '')
|
||||||
|
admin.set_password('j7Th5TfCGUZmQ7U')
|
||||||
|
admin.save()
|
||||||
|
print('Created {}@{} with private key {} and public key {}'.format(admin.username, admin.domain,
|
||||||
|
admin.private_key,
|
||||||
|
admin.public_identity.public_key))
|
||||||
|
except Exception as e:
|
||||||
|
print('Admin user already exists, skipping')
|
||||||
|
|
||||||
|
from hostadmin.models import Domain
|
||||||
|
try:
|
||||||
|
domain1 = Domain.objects.create(name='localhost', owner=admin, open_registration=False)
|
||||||
|
domain1.save()
|
||||||
|
print('Created domain {} with closed registration'.format(domain1.name))
|
||||||
|
except Exception as e:
|
||||||
|
print('Domain localhost already exists, skipping')
|
||||||
|
|
||||||
|
domain2 = None
|
||||||
|
try:
|
||||||
|
if Domain.objects.filter(name='b.localhost').exists():
|
||||||
|
domain2 = Domain.objects.get(name='b.localhost')
|
||||||
|
domain2 = Domain.objects.create(name='b.localhost', owner=admin, open_registration=True)
|
||||||
|
domain2.save()
|
||||||
|
print('Created domain {} with open registration'.format(domain2.name))
|
||||||
|
except Exception as e:
|
||||||
|
print('Domain b.localhost already exists, skipping')
|
||||||
|
|
||||||
|
user = None
|
||||||
|
try:
|
||||||
|
if ToolshedUser.objects.filter(username='test_b').exists():
|
||||||
|
user = ToolshedUser.objects.get(username='test_b')
|
||||||
|
user = ToolshedUser.objects.create_user('test_b', 'testb@example.com', '', domain=domain2.name,
|
||||||
|
private_key='2ec1e7d5f5b8d5f87233944970d57f942095fe9e6c4fc49edde61fcd3fb1bf40')
|
||||||
|
user.set_password('test_b')
|
||||||
|
user.save()
|
||||||
|
print('Created user {}@{} with private key {} and public key {}'.format(user.username, user.domain,
|
||||||
|
user.private_key,
|
||||||
|
user.public_identity.public_key))
|
||||||
|
except Exception as e:
|
||||||
|
print('User foobar already exists, skipping')
|
||||||
|
|
||||||
|
from authentication.models import KnownIdentity
|
||||||
|
identity = None
|
||||||
|
try:
|
||||||
|
if KnownIdentity.objects.filter(username='test_a').exists():
|
||||||
|
identity = KnownIdentity.objects.get(username='test_a')
|
||||||
|
identity = KnownIdentity.objects.create(username='test_a', domain='a.localhost',
|
||||||
|
public_key='cdcf6f1897b0ab3a123047d9b707d4123f01daf41921dbe787d1d0697f0ee42b')
|
||||||
|
# pk '4ab79601edc400fd0263c7d5fd045ea7f5de28f1dd8905e2479f96893f3257d8'
|
||||||
|
identity.save()
|
||||||
|
print('Created identity {}@{} with public key {}'.format(identity.username, identity.domain,
|
||||||
|
identity.public_key))
|
||||||
|
except Exception as e:
|
||||||
|
print('Identity already exists, skipping')
|
||||||
|
|
||||||
|
from toolshed.models import Category
|
||||||
|
category1 = None
|
||||||
|
try:
|
||||||
|
if Category.objects.filter(name='Test Category').exists():
|
||||||
|
category1 = Category.objects.get(name='Test Category')
|
||||||
|
category1 = Category.objects.get_or_create(name='Test Category', origin='test')[0]
|
||||||
|
except Exception as e:
|
||||||
|
print('Category already exists, skipping')
|
||||||
|
|
||||||
|
from toolshed.models import InventoryItem
|
||||||
|
item1 = None
|
||||||
|
try:
|
||||||
|
if InventoryItem.objects.filter(name='Test Item 3').exists():
|
||||||
|
item1 = InventoryItem.objects.get(name='Test Item 3')
|
||||||
|
item1 = InventoryItem.objects.create(name='Test Item 3', description='This is a test item', category=category1,
|
||||||
|
availability_policy='sell', owned_quantity=1, owner=user)
|
||||||
|
item1.save()
|
||||||
|
except Exception as e:
|
||||||
|
print('Item already exists, skipping')
|
||||||
|
|
||||||
|
try:
|
||||||
|
item2 = InventoryItem.objects.create(name='Test Item 4', description='This is a test item', category=category1,
|
||||||
|
availability_policy='lend', owned_quantity=1, owner=user)
|
||||||
|
item2.save()
|
||||||
|
print('Created test items with IDs "{}" and "{}"'.format(item1.id, item2.id))
|
||||||
|
except Exception as e:
|
||||||
|
print('Item already exists, skipping')
|
||||||
|
|
||||||
|
try:
|
||||||
|
user.friends.add(identity)
|
||||||
|
print('Added identity {} to friends list of user {}'.format(identity.username, user.username))
|
||||||
|
except Exception as e:
|
||||||
|
print('Identity already in friends list, skipping')
|
||||||
|
|
||||||
|
from files.models import File
|
||||||
|
try:
|
||||||
|
file1 = File.objects.create(mime_type='text/plain', data=base64.b64encode(b'testcontent1').decode('utf-8'))
|
||||||
|
file1.save()
|
||||||
|
print(f'Created file at /{file1.hash[:2]}/{file1.hash[2:4]}/{file1.hash[4:6]}/{file1.hash[6:]}')
|
||||||
|
except Exception as e:
|
||||||
|
print('File already exists, skipping')
|
|
@ -8,6 +8,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- ../backend:/code
|
- ../backend:/code
|
||||||
- ../deploy/dev/instance_a/a.env:/code/.env
|
- ../deploy/dev/instance_a/a.env:/code/.env
|
||||||
|
- ../deploy/dev/instance_a/testdata.py:/code/testdata.py
|
||||||
- ../deploy/dev/instance_a/a.sqlite3:/code/db.sqlite3
|
- ../deploy/dev/instance_a/a.sqlite3:/code/db.sqlite3
|
||||||
expose:
|
expose:
|
||||||
- 8000
|
- 8000
|
||||||
|
@ -20,6 +21,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- ../backend:/code
|
- ../backend:/code
|
||||||
- ../deploy/dev/instance_b/b.env:/code/.env
|
- ../deploy/dev/instance_b/b.env:/code/.env
|
||||||
|
- ../deploy/dev/instance_b/testdata.py:/code/testdata.py
|
||||||
- ../deploy/dev/instance_b/b.sqlite3:/code/db.sqlite3
|
- ../deploy/dev/instance_b/b.sqlite3:/code/db.sqlite3
|
||||||
expose:
|
expose:
|
||||||
- 8000
|
- 8000
|
||||||
|
@ -30,11 +32,11 @@ services:
|
||||||
context: ../frontend/
|
context: ../frontend/
|
||||||
dockerfile: ../deploy/dev/Dockerfile.frontend
|
dockerfile: ../deploy/dev/Dockerfile.frontend
|
||||||
volumes:
|
volumes:
|
||||||
- ../frontend:/app:ro
|
- ../frontend:/app
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
expose:
|
expose:
|
||||||
- 5173
|
- 5173
|
||||||
command: npm run dev -- --host
|
command: bash -c "npm install && npm run dev -- --host"
|
||||||
|
|
||||||
wiki:
|
wiki:
|
||||||
build:
|
build:
|
||||||
|
|
|
@ -9,6 +9,12 @@
|
||||||
<li class="sidebar-header">
|
<li class="sidebar-header">
|
||||||
Tools & Components
|
Tools & Components
|
||||||
</li>
|
</li>
|
||||||
|
<li class="sidebar-item">
|
||||||
|
<router-link to="/inventory" class="sidebar-link">
|
||||||
|
<b-icon-archive class="bi-valign-middle"></b-icon-archive>
|
||||||
|
<span class="align-middle">Inventory</span>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
<li class="sidebar-item">
|
<li class="sidebar-item">
|
||||||
<router-link to="/friends" class="sidebar-link">
|
<router-link to="/friends" class="sidebar-link">
|
||||||
<b-icon-people class="bi-valign-middle"></b-icon-people>
|
<b-icon-people class="bi-valign-middle"></b-icon-people>
|
||||||
|
|
|
@ -13,9 +13,13 @@ function get_prefered_server() {
|
||||||
request.open('GET', '/local/dns', false);
|
request.open('GET', '/local/dns', false);
|
||||||
request.send(null);
|
request.send(null);
|
||||||
if (request.status === 200) {
|
if (request.status === 200) {
|
||||||
const servers = JSON.parse(request.responseText);
|
try {
|
||||||
if (servers && servers.length > 0) {
|
const servers = JSON.parse(request.responseText);
|
||||||
return servers;
|
if (servers && servers.length > 0) {
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ['1.1.1.1', '8.8.8.8'];
|
return ['1.1.1.1', '8.8.8.8'];
|
||||||
|
|
|
@ -4,10 +4,12 @@ import Login from '@/views/Login.vue';
|
||||||
import Register from '@/views/Register.vue';
|
import Register from '@/views/Register.vue';
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
import Friends from "@/views/Friends.vue";
|
import Friends from "@/views/Friends.vue";
|
||||||
|
import Inventory from '@/views/Inventory.vue';
|
||||||
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{path: '/', component: Dashboard, meta: {requiresAuth: true}},
|
{path: '/', component: Dashboard, meta: {requiresAuth: true}},
|
||||||
|
{path: '/inventory', component: Inventory, meta: {requiresAuth: true}},
|
||||||
{path: '/friends', component: Friends, meta: {requiresAuth: true}},
|
{path: '/friends', component: Friends, meta: {requiresAuth: true}},
|
||||||
{path: '/login', component: Login, meta: {requiresAuth: false}},
|
{path: '/login', component: Login, meta: {requiresAuth: false}},
|
||||||
{path: '/register', component: Register, meta: {requiresAuth: false}},
|
{path: '/register', component: Register, meta: {requiresAuth: false}},
|
||||||
|
|
|
@ -13,10 +13,13 @@ export default createStore({
|
||||||
token: null,
|
token: null,
|
||||||
keypair: null,
|
keypair: null,
|
||||||
remember: false,
|
remember: false,
|
||||||
|
friends: [],
|
||||||
|
item_map: {},
|
||||||
home_servers: null,
|
home_servers: null,
|
||||||
all_friends_servers: null,
|
all_friends_servers: null,
|
||||||
resolver: new FallBackResolver(),
|
resolver: new FallBackResolver(),
|
||||||
unreachable_neighbors: new NeighborsCache(),
|
unreachable_neighbors: new NeighborsCache(),
|
||||||
|
availability_policies: [],
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setUser(state, user) {
|
setUser(state, user) {
|
||||||
|
@ -43,6 +46,9 @@ export default createStore({
|
||||||
}
|
}
|
||||||
localStorage.setItem('remember', remember);
|
localStorage.setItem('remember', remember);
|
||||||
},
|
},
|
||||||
|
setInventoryItems(state, {url, items}) {
|
||||||
|
state.item_map[url] = items;
|
||||||
|
},
|
||||||
setFriends(state, friends) {
|
setFriends(state, friends) {
|
||||||
state.friends = friends;
|
state.friends = friends;
|
||||||
},
|
},
|
||||||
|
@ -52,6 +58,9 @@ export default createStore({
|
||||||
setAllFriendsServers(state, servers) {
|
setAllFriendsServers(state, servers) {
|
||||||
state.all_friends_servers = servers;
|
state.all_friends_servers = servers;
|
||||||
},
|
},
|
||||||
|
setAvailabilityPolicies(state, availability_policies) {
|
||||||
|
state.availability_policies = availability_policies;
|
||||||
|
},
|
||||||
logout(state) {
|
logout(state) {
|
||||||
state.user = null;
|
state.user = null;
|
||||||
state.token = null;
|
state.token = null;
|
||||||
|
@ -128,6 +137,13 @@ export default createStore({
|
||||||
async getFriendServers({state, dispatch, commit}, {username}) {
|
async getFriendServers({state, dispatch, commit}, {username}) {
|
||||||
return dispatch('lookupServer', {username}).then(servers => new ServerSet(servers, state.unreachable_neighbors))
|
return dispatch('lookupServer', {username}).then(servers => new ServerSet(servers, state.unreachable_neighbors))
|
||||||
},
|
},
|
||||||
|
async fetchInventoryItems({commit, dispatch, getters}) {
|
||||||
|
const servers = await dispatch('getHomeServers')
|
||||||
|
const items = await servers.get(getters.signAuth, '/api/inventory_items/')
|
||||||
|
items.map(item => item.files.map(file => file.owner = item.owner))
|
||||||
|
commit('setInventoryItems', {url: '/', items})
|
||||||
|
return items
|
||||||
|
},
|
||||||
async fetchFriends({commit, dispatch, getters, state}) {
|
async fetchFriends({commit, dispatch, getters, state}) {
|
||||||
const servers = await dispatch('getHomeServers')
|
const servers = await dispatch('getHomeServers')
|
||||||
const data = await servers.get(getters.signAuth, '/api/friends/')
|
const data = await servers.get(getters.signAuth, '/api/friends/')
|
||||||
|
@ -205,5 +221,13 @@ export default createStore({
|
||||||
nullAuth(state) {
|
nullAuth(state) {
|
||||||
return createNullAuth({})
|
return createNullAuth({})
|
||||||
},
|
},
|
||||||
|
inventory_items(state) {
|
||||||
|
return state.item_map['/'] || []
|
||||||
|
},
|
||||||
|
loaded_items(state) {
|
||||||
|
return Object.entries(state.item_map).reduce((acc, [url, items]) => {
|
||||||
|
return acc.concat(items)
|
||||||
|
}, [])
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
70
frontend/src/views/Inventory.vue
Normal file
70
frontend/src/views/Inventory.vue
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<BaseLayout>
|
||||||
|
<main class="content">
|
||||||
|
<div class="container-fluid p-0">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-xl-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="card-title">{{ user }}'s Inventory</h5>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:40%;">Name</th>
|
||||||
|
<th style="width:25%">Availability Policy</th>
|
||||||
|
<th class="d-none d-md-table-cell" style="width:25%">Amount</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="item in inventory_items" :key="item.id">
|
||||||
|
<td>
|
||||||
|
{{ item.name }}
|
||||||
|
</td>
|
||||||
|
<td class="d-none d-md-table-cell">{{ item.availability_policy }}</td>
|
||||||
|
<td class="d-none d-md-table-cell">{{ item.owned_quantity }}</td>
|
||||||
|
<td class="table-action">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<button class="btn" @click="fetchInventoryItems">Refresh</button>
|
||||||
|
<router-link to="/inventory/new" class="btn btn-primary">Add</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</BaseLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapActions, mapGetters, mapMutations, mapState} from "vuex";
|
||||||
|
import * as BIcons from "bootstrap-icons-vue";
|
||||||
|
import BaseLayout from "@/components/BaseLayout.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Inventory",
|
||||||
|
components: {
|
||||||
|
BaseLayout,
|
||||||
|
...BIcons
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(["inventory_items", "loaded_items"]),
|
||||||
|
...mapState(["user"]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(["fetchInventoryItems", "deleteInventoryItem"]),
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.fetchInventoryItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
Loading…
Reference in a new issue