diff --git a/.design/DESIGN.md b/.design/DESIGN.md new file mode 100644 index 00000000..50e3cb2d --- /dev/null +++ b/.design/DESIGN.md @@ -0,0 +1,51 @@ +# Databag Design Guidelines & Resources + +A collection of design contribution guidelines and resources for our Databag product. + +> **All participating designers are highly encouraged to shape and evolve these guidelines!** + +## Welcome + +Databag is a product targeted for the decentralized web community. It is a federeated messenger that gives people back their privacy and control of their data. + +## How to contribute design + +1. Check out open [issues](https://github.com/balzack/databag/issues) here on GitHub (we label them with `design: required`) +2. Feel free to open an issue on your own if you find something you would like to contribute to the project and use the `design: idea` label for it. +3. There are no existing figma files yet, for now create new ones and share them publicly +4. Add your contributions to an issue and we promise we will review your contribution carefully and foster discussions + +**We encourage you to:** + +- Get in touch with the team by starting a discussion on [GitHub](https://github.com/balzack/databag/discussions). + +## Target audience + +The initial users are typically very technical, but hopefully this product will be embedded into consumer electronics and reach a much less technical audience. The design may benefit by exposing some technical attributes. For example, with a self-hosted product the users my be interested in knowing where their data actually lives. + +## Design relevant materials + +Here is a list of design relevant information and materials: + +### Fonts + +Currently we're using Roboto everywhere. + +### Colors + +We're using shades of green deliberately staying away from the corporate blue. The current background color is (#8fbea7) with the primary button color (#448866). + +### Logos + +We don't really have a logo yet, and are currently just using a scaled image from the app as the [icon](https://github.com/balzack/databag/blob/main/doc/icon.png) + +### Design Files, Screenshots, etc + +We don't have any design files yet. Initially you can reference screenshots for the [browser app](https://github.com/balzack/databag/blob/main/doc/browser.png) and [mobile app](https://github.com/balzack/databag/blob/main/doc/mobile.png) + +## License + +All design work is licensed under the +[Apache-2.0](https://github.com/balzack/databag/blob/main/LICENSE) + +[(Back to top)](#-table-of-contents) diff --git a/README.md b/README.md index 67b91e35..511b5482 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ + +[![contribute.design](https://contribute.design/api/shield/balzack/databag)](https://contribute.design/balzack/databag) +

Databag

@@ -19,7 +22,7 @@ Databag is a self-hosted messaging service. Notable features include: - Public-private key based identity (not bound to any blockchain or hosting domain) - Federated (accounts on different nodes can communicate) -- Topic based threads (messages grouped by topic not contacts) +- Topic based threads (messages organized by topic not contacts) - Lightweight (server runs on a raspberry pi zero v1.3) - Decentralized (direct communication between app and contact's node) - Low latency (use of websockets for push events to avoid polling) @@ -30,10 +33,10 @@ Databag is a self-hosted messaging service. Notable features include:

- + - +

@@ -43,7 +46,10 @@ The app is available in the google and apple stores. You can also test out the p To use databag, you will need a DNS name pointing to your node with a certificate. You can deploy a node manually, but you will have a much easier time using a container service. Containers for arm64 and amd64 are available [here](https://hub.docker.com/r/balzack/databag/tags). -Instruction for installing without a container on a Raspberry Pi Zero are [here](/doc/pizero.md). +### Docker Compose Command + +From the net/container sub directory: + - sudo docker-compose -f compose.yaml -p databag up ### Example with Portainer and Nginx Proxy Manager @@ -71,3 +77,9 @@ From Your Browser: - Click 'Save' - Click the user icon to generate a new account link - Follow the link to create an account + +### Other installation options + +Instruction for installing without a container on a Raspberry Pi Zero are [here](/doc/pizero.md). + +Instruction for installing without a container in AWS are [here](/doc/aws.md). diff --git a/app/mobile/ios/Databag/Info.plist b/app/mobile/ios/Databag/Info.plist index ded82efc..879e8c36 100644 --- a/app/mobile/ios/Databag/Info.plist +++ b/app/mobile/ios/Databag/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.4 + 1.5 CFBundleSignature ???? CFBundleVersion diff --git a/app/mobile/src/access/admin/Admin.jsx b/app/mobile/src/access/admin/Admin.jsx index 65228e67..a3694532 100644 --- a/app/mobile/src/access/admin/Admin.jsx +++ b/app/mobile/src/access/admin/Admin.jsx @@ -69,6 +69,9 @@ export function Admin() { )} + + v{ state.version } + ); diff --git a/app/mobile/src/access/admin/Admin.styled.js b/app/mobile/src/access/admin/Admin.styled.js index 84402fb3..313c7a65 100644 --- a/app/mobile/src/access/admin/Admin.styled.js +++ b/app/mobile/src/access/admin/Admin.styled.js @@ -108,6 +108,13 @@ export const styles = StyleSheet.create({ nologintext: { color: Colors.disabled, }, - + version: { + display: 'flex', + alignItems: 'flex-end', + }, + versiontext: { + color: Colors.grey, + fontSize: 14, + }, }) diff --git a/app/mobile/src/access/admin/useAdmin.hook.js b/app/mobile/src/access/admin/useAdmin.hook.js index 27530e40..5a9644b1 100644 --- a/app/mobile/src/access/admin/useAdmin.hook.js +++ b/app/mobile/src/access/admin/useAdmin.hook.js @@ -17,12 +17,17 @@ export function useAdmin() { server: null, token: null, plainText: false, + version: null, }); const updateState = (value) => { setState((s) => ({ ...s, ...value })); } + useEffect(() => { + updateState({ version: app.state.version }); + }, [app]); + const checkStatus = async () => { try { updateState({ unclaimed: status }); diff --git a/app/mobile/src/context/useAppContext.hook.js b/app/mobile/src/context/useAppContext.hook.js index eaeb2804..1b2dd8b8 100644 --- a/app/mobile/src/context/useAppContext.hook.js +++ b/app/mobile/src/context/useAppContext.hook.js @@ -20,6 +20,8 @@ export function useAppContext() { loginTimestamp: null, disconnected: null, deviceToken: null, + loggingOut: false, + version: getVersion(), }); const store = useContext(StoreContext); const account = useContext(AccountContext); @@ -27,6 +29,7 @@ export function useAppContext() { const card = useContext(CardContext); const channel = useContext(ChannelContext); const count = useRef(0); + const delay = useRef(0); const ws = useRef(null); @@ -83,7 +86,7 @@ export function useAppContext() { username: getUsername, create: async (server, username, password, token) => { await addAccount(server, username, password, token); - const access = await setLogin(username, server, password, getApplicatioName(), getVersion(), getDeviceId(), state.deviceToken, notifications) + const access = await setLogin(username, server, password, getApplicationName(), getVersion(), getDeviceId(), state.deviceToken, notifications) await store.actions.setSession({ ...access, server}); await setSession({ ...access, server }); if (access.pushSupported) { @@ -108,6 +111,7 @@ export function useAppContext() { } }, logout: async () => { + updateState({ loggingOut: true }); try { await messaging().deleteToken(); const token = await messaging().getToken(); @@ -119,6 +123,7 @@ export function useAppContext() { } await clearSession(); await store.actions.clearSession(); + updateState({ loggingOut: false }); }, remove: async () => { await removeProfile(state.server, state.appToken); @@ -131,6 +136,7 @@ export function useAppContext() { clearWebsocket(); ws.current = new WebSocket(`wss://${server}/status`); ws.current.onmessage = (ev) => { + delay.current = 0; try { const rev = JSON.parse(ev.data); try { @@ -159,9 +165,10 @@ export function useAppContext() { ws.current.onclose = () => {} ws.current.onopen = () => {} ws.current.onerror = () => {} + delay.current = 1; setWebsocket(server, token); } - }, 1000) + }, 1000 * delay.current) } ws.current.onopen = () => { ws.current.send(JSON.stringify({ AppToken: token })) diff --git a/app/mobile/src/session/conversation/Conversation.jsx b/app/mobile/src/session/conversation/Conversation.jsx index 73195c5d..02bedf20 100644 --- a/app/mobile/src/session/conversation/Conversation.jsx +++ b/app/mobile/src/session/conversation/Conversation.jsx @@ -1,5 +1,4 @@ import { Keyboard, KeyboardAvoidingView, ActivityIndicator, Modal, Platform, TextInput, View, TouchableOpacity, Text, } from 'react-native'; -import { useKeepAwake } from 'expo-keep-awake'; import { FlatList, ScrollView } from '@stream-io/flat-list-mvcp'; import { memo, useState, useRef, useEffect } from 'react'; import { useConversation } from './useConversation.hook'; @@ -59,8 +58,6 @@ export function ConversationBody() { }; }, []); - useKeepAwake(); - const latch = () => { if (!state.momentum) { actions.latch(); diff --git a/app/mobile/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx b/app/mobile/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx index 3a45fb58..e4185347 100644 --- a/app/mobile/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx +++ b/app/mobile/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx @@ -1,5 +1,6 @@ import { Image, View, Text, TouchableOpacity } from 'react-native'; import { useRef } from 'react'; +import { useKeepAwake } from 'expo-keep-awake'; import Colors from 'constants/Colors'; import { Video, AVPlaybackStatus } from 'expo-av'; import { useAudioAsset } from './useAudioAsset.hook'; @@ -12,6 +13,8 @@ export function AudioAsset({ topicId, asset, dismiss }) { const { state, actions } = useAudioAsset(topicId, asset); + useKeepAwake(); + const player = useRef(null); return ( diff --git a/app/mobile/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx b/app/mobile/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx index b5df8f7b..2605c1e8 100644 --- a/app/mobile/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx +++ b/app/mobile/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx @@ -1,5 +1,6 @@ import { ActivityIndicator, Image, View, TouchableOpacity } from 'react-native'; import Colors from 'constants/Colors'; +import { useKeepAwake } from 'expo-keep-awake'; import { Video, AVPlaybackStatus } from 'expo-av'; import { useVideoAsset } from './useVideoAsset.hook'; import { styles } from './VideoAsset.styled'; @@ -9,6 +10,8 @@ export function VideoAsset({ topicId, asset, dismiss }) { const { state, actions } = useVideoAsset(topicId, asset); + useKeepAwake(); + return ( diff --git a/app/mobile/src/session/profile/Profile.jsx b/app/mobile/src/session/profile/Profile.jsx index 7ee75efa..42e77c05 100644 --- a/app/mobile/src/session/profile/Profile.jsx +++ b/app/mobile/src/session/profile/Profile.jsx @@ -1,5 +1,5 @@ import { useEffect, useContext } from 'react'; -import { KeyboardAvoidingView, Modal, Alert, TextInput, ScrollView, View, Switch, TouchableOpacity, Text } from 'react-native'; +import { ActivityIndicator, KeyboardAvoidingView, Modal, Alert, TextInput, ScrollView, View, Switch, TouchableOpacity, Text } from 'react-native'; import { styles } from './Profile.styled'; import { useProfile } from './useProfile.hook'; import Ionicons from '@expo/vector-icons/AntDesign'; @@ -21,9 +21,16 @@ export function Profile({ navigation }) { ), headerRight: () => ( - - - + <> + { state.loggingOut && ( + + )} + { !state.loggingOut && ( + + + + )} + ), }); } @@ -71,9 +78,14 @@ export function Profile({ navigation }) { { `${state.handle}@${state.node}` } - - - + { state.loggingOut && ( + + )} + { !state.loggingOut && ( + + + + )} diff --git a/app/mobile/src/session/profile/Profile.styled.js b/app/mobile/src/session/profile/Profile.styled.js index f08b2d2a..32d4125b 100644 --- a/app/mobile/src/session/profile/Profile.styled.js +++ b/app/mobile/src/session/profile/Profile.styled.js @@ -44,6 +44,7 @@ export const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'flex-end', justifyContent: 'center', + height: 32, }, headerText: { paddingLeft: 16, diff --git a/app/mobile/src/session/profile/useProfile.hook.js b/app/mobile/src/session/profile/useProfile.hook.js index 583ddefc..041e40a5 100644 --- a/app/mobile/src/session/profile/useProfile.hook.js +++ b/app/mobile/src/session/profile/useProfile.hook.js @@ -14,6 +14,7 @@ export function useProfile() { showDelete: false, tabbed: null, confirmDelete: null, + logginOut: false, }); const app = useContext(AppContext); @@ -42,8 +43,8 @@ export function useProfile() { }, [profile]); useEffect(() => { - const { disconnected } = app.state; - updateState({ disconnected }); + const { disconnected, loggingOut } = app.state; + updateState({ disconnected, loggingOut }); }, [app]); const actions = { diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/doc/CHANGELOG.md @@ -0,0 +1 @@ + diff --git a/doc/aws.md b/doc/aws.md new file mode 100644 index 00000000..534b74b9 --- /dev/null +++ b/doc/aws.md @@ -0,0 +1,97 @@ +# Install Databag in AWS + +These instructions assume you have the following setup: + - an AMD64 Ubuntu EC2 instance with incoming ports 443 and 80
+ - an EFS instance
+ - a domain name pointing the the IP of your EC2 instance
+ +## Step 1: obtain cert + sudo apt-get install certbot
+ sudo certbot certonly --standalone -d [dns name]
+ +## Step 2: install databag dependencies + sudo apt-get -y install ffmpeg
+ sudo apt-get -y install curl
+ sudo apt-get -y install net-tools
+ sudo apt-get -y install jq
+ sudo apt-get -y install netcat
+ sudo apt-get -y install unzip
+ sudo apt-get -y install wget
+ sudo apt-get -y install git
+ sudo apt-get -y install vim
+ sudo apt-get -y install fail2ban
+ sudo apt-get -y install imagemagick-6.q16
+ sudo apt-get -y install build-essential
+ sudo apt-get -y install sqlite3
+ sudo apt-get -y install openssh-client
+ apt-get -y install npm
+ apt-get -y upgrade
+ npm install --global yarn
+ npm install -g n
+ n stable
+ +## Step 3: download and install golang + wget https://go.dev/dl/go1.19.3.linux-amd64.tar.gz
+ sudo tar -C /usr/local -xzf go1.19.3.linux-amd64.tar.gz
+ +## Step 4: clone and build the server + mkdir /app
+ cd /app
+ git clone https://github.com/balzack/databag.git
+ cd /app/databag/net/web
+ yarn config set network-timeout 300000
+ yarn --cwd /app/databag/net/web install
+ yarn --cwd /app/databag/net/web build
+ cd /app/databag/net/server
+ /usr/local/go/bin/go build databag
+ +## Step 5: setup databag paths + mkdir -p /var/lib/databag/assets
+ mkdir -p /opt/databag/transform
+ cp /app/databag/net/container/transform/* /opt/databag/transform/
+ +## Step 6: mount EFS to store assets + sudo apt-get update
+ sudo apt-get -y install git binutils
+ git clone https://github.com/aws/efs-utils
+ cd efs-utils
+ ./build-deb.sh
+ sudo apt-get -y install ./build/amazon-efs-utils*deb
+ sudo mount -t efs file-system-id /var/lib/databag/assets
+ +## Step 7: initialize the internal datbase + sqlite3 /var/lib/databag/databag.db "VACUUM;"
+ sqlite3 /var/lib/databag/databag.db "CREATE TABLE IF NOT EXISTS 'configs' ('id' integer NOT NULL UNIQUE,'config_id' text NOT NULL,'str_value' text,'num_value' integer,'bool_value' numeric,'bin_value' blob,PRIMARY KEY ('id'));"
+ sqlite3 /var/lib/databag/databag.db "CREATE UNIQUE INDEX IF NOT EXISTS 'idx_configs_config_id' ON 'configs'('config_id');"
+ sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, str_value) values ('asset_path', '/var/lib/databag/assets');"
+ sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, str_value) values ('script_path', '/opt/databag/transform/');"
+ +## Step 8: launch the server + cd /app/databag/net/server
+ nohup nice -n -5 /usr/local/go/bin/go run databag [dns name] &
+ +## Step 9: configure the server + Open your brower to https://[dns name]
+ Click the 'cog' in the upper right
+ Set an admin password
+ Select the 'cog' to bring up the settings modal
+ - set your hostname as [dns name]
+ - set the key to RSA 2048
+ - enable push notifications
+ - enable images
+ - disable audio
+ - disable video
+ +## Step 10: create accounts + Still in the admin dashboard
+ Click the 'add-user' button
+ Open the link in a new tab
+ Set a username and password
+ Setup your profile
+ Connect with contacts on other federated instances
+ +## Step 11: host for your friends and family + Back in the admin dashboard
+ Click the 'add-user' and send the link to anyone you want to host
+ + diff --git a/net/container/compose.yaml b/net/container/compose.yaml new file mode 100644 index 00000000..a1b33470 --- /dev/null +++ b/net/container/compose.yaml @@ -0,0 +1,10 @@ +version: "3.9" +services: + databag: + container_name: databag + image: balzack/databag:latest + ports: + - "7000:7000" + volumes: + - ./databag-data:/data + diff --git a/net/server/main.go b/net/server/main.go index 04f5570d..e914f69b 100644 --- a/net/server/main.go +++ b/net/server/main.go @@ -21,11 +21,11 @@ func main() { args := os.Args if len(args) == 3 { port := ":" + args[2] - path := "etc/letsencrypt/live/" + args[1] + path := "/etc/letsencrypt/live/" + args[1] log.Printf("starting server at: " + path + " " + port); log.Fatal(http.ListenAndServeTLS(port, path + "/fullchain.pem", path + "/privkey.pem", handlers.CORS(origins, methods)(router))) } else if len(args) == 2 { - path := "etc/letsencrypt/live/" + args[1] + path := "/etc/letsencrypt/live/" + args[1] log.Printf("starting server at: " + path); log.Fatal(http.ListenAndServeTLS(":443", path + "/fullchain.pem", path + "/privkey.pem", handlers.CORS(origins, methods)(router))) } else {