Compare commits
2 commits
main
...
linked_ins
Author | SHA1 | Date | |
---|---|---|---|
0c84d9b604 | |||
af5f8b0538 |
14 changed files with 136 additions and 8 deletions
|
@ -133,6 +133,20 @@ impl Settings {
|
||||||
fs::read_to_string(CONFIG_FILE)
|
fs::read_to_string(CONFIG_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_allowed_instances(&self) -> Vec<String> {
|
||||||
|
let mut allowed_instances: Vec<String> = self
|
||||||
|
.federation
|
||||||
|
.allowed_instances
|
||||||
|
.split(',')
|
||||||
|
.map(|d| d.to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// The defaults.hjson config always returns a [""]
|
||||||
|
allowed_instances.retain(|d| !d.eq(""));
|
||||||
|
|
||||||
|
allowed_instances
|
||||||
|
}
|
||||||
|
|
||||||
pub fn save_config_file(data: &str) -> Result<String, Error> {
|
pub fn save_config_file(data: &str) -> Result<String, Error> {
|
||||||
fs::write(CONFIG_FILE, data)?;
|
fs::write(CONFIG_FILE, data)?;
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,7 @@ pub struct GetSiteResponse {
|
||||||
pub online: usize,
|
pub online: usize,
|
||||||
version: String,
|
version: String,
|
||||||
my_user: Option<User_>,
|
my_user: Option<User_>,
|
||||||
|
federated_instances: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -433,6 +434,7 @@ impl Perform for Oper<GetSite> {
|
||||||
online,
|
online,
|
||||||
version: version::VERSION.to_string(),
|
version: version::VERSION.to_string(),
|
||||||
my_user,
|
my_user,
|
||||||
|
federated_instances: Settings::get().get_allowed_instances(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -659,6 +661,7 @@ impl Perform for Oper<TransferSite> {
|
||||||
online: 0,
|
online: 0,
|
||||||
version: version::VERSION.to_string(),
|
version: version::VERSION.to_string(),
|
||||||
my_user: Some(user),
|
my_user: Some(user),
|
||||||
|
federated_instances: Settings::get().get_allowed_instances(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,12 +70,8 @@ fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
|
||||||
return Err(anyhow!("invalid apub id scheme: {:?}", apub_id.scheme()).into());
|
return Err(anyhow!("invalid apub id scheme: {:?}", apub_id.scheme()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut allowed_instances: Vec<String> = Settings::get()
|
let mut allowed_instances: Vec<String> = Settings::get().get_allowed_instances();
|
||||||
.federation
|
|
||||||
.allowed_instances
|
|
||||||
.split(',')
|
|
||||||
.map(|d| d.to_string())
|
|
||||||
.collect();
|
|
||||||
// need to allow this explicitly because apub activities might contain objects from our local
|
// need to allow this explicitly because apub activities might contain objects from our local
|
||||||
// instance. replace is needed to remove the port in our federation test setup.
|
// instance. replace is needed to remove the port in our federation test setup.
|
||||||
let settings = Settings::get();
|
let settings = Settings::get();
|
||||||
|
|
|
@ -40,7 +40,8 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
)
|
)
|
||||||
.route("/search", web::get().to(index))
|
.route("/search", web::get().to(index))
|
||||||
.route("/sponsors", web::get().to(index))
|
.route("/sponsors", web::get().to(index))
|
||||||
.route("/password_change/{token}", web::get().to(index));
|
.route("/password_change/{token}", web::get().to(index))
|
||||||
|
.route("/instances", web::get().to(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn index() -> Result<NamedFile, Error> {
|
async fn index() -> Result<NamedFile, Error> {
|
||||||
|
|
1
ui/src/components/admin-settings.tsx
vendored
1
ui/src/components/admin-settings.tsx
vendored
|
@ -48,6 +48,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
banned: [],
|
banned: [],
|
||||||
online: null,
|
online: null,
|
||||||
version: null,
|
version: null,
|
||||||
|
federated_instances: null,
|
||||||
},
|
},
|
||||||
siteConfigForm: {
|
siteConfigForm: {
|
||||||
config_hjson: null,
|
config_hjson: null,
|
||||||
|
|
5
ui/src/components/footer.tsx
vendored
5
ui/src/components/footer.tsx
vendored
|
@ -51,6 +51,11 @@ export class Footer extends Component<any, FooterState> {
|
||||||
{i18n.t('modlog')}
|
{i18n.t('modlog')}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<Link class="nav-link" to="/instances">
|
||||||
|
{i18n.t('instances')}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href={'/docs/index.html'}>
|
<a class="nav-link" href={'/docs/index.html'}>
|
||||||
{i18n.t('docs')}
|
{i18n.t('docs')}
|
||||||
|
|
98
ui/src/components/instances.tsx
vendored
Normal file
98
ui/src/components/instances.tsx
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import { Component } from 'inferno';
|
||||||
|
import { Helmet } from 'inferno-helmet';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
UserOperation,
|
||||||
|
WebSocketJsonResponse,
|
||||||
|
GetSiteResponse,
|
||||||
|
} from '../interfaces';
|
||||||
|
import { WebSocketService } from '../services';
|
||||||
|
import { wsJsonToRes, toast } from '../utils';
|
||||||
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
|
interface InstancesState {
|
||||||
|
loading: boolean;
|
||||||
|
siteRes: GetSiteResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Instances extends Component<any, InstancesState> {
|
||||||
|
private subscription: Subscription;
|
||||||
|
private emptyState: InstancesState = {
|
||||||
|
loading: true,
|
||||||
|
siteRes: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
this.state = this.emptyState;
|
||||||
|
this.subscription = WebSocketService.Instance.subject
|
||||||
|
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
|
||||||
|
.subscribe(
|
||||||
|
msg => this.parseMessage(msg),
|
||||||
|
err => console.error(err),
|
||||||
|
() => console.log('complete')
|
||||||
|
);
|
||||||
|
|
||||||
|
WebSocketService.Instance.getSite();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
get documentTitle(): string {
|
||||||
|
if (this.state.siteRes) {
|
||||||
|
return `${i18n.t('instances')} - ${this.state.siteRes.site.name}`;
|
||||||
|
} else {
|
||||||
|
return 'Lemmy';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div class="container">
|
||||||
|
<Helmet title={this.documentTitle} />
|
||||||
|
{this.state.loading ? (
|
||||||
|
<h5 class="">
|
||||||
|
<svg class="icon icon-spinner spin">
|
||||||
|
<use xlinkHref="#icon-spinner"></use>
|
||||||
|
</svg>
|
||||||
|
</h5>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<h5>{i18n.t('linked_instances')}</h5>
|
||||||
|
{this.state.siteRes &&
|
||||||
|
this.state.siteRes.federated_instances.length ? (
|
||||||
|
<ul>
|
||||||
|
{this.state.siteRes.federated_instances.map(i => (
|
||||||
|
<li>
|
||||||
|
<a href={`https://${i}`} target="_blank" rel="noopener">
|
||||||
|
{i}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<div>{i18n.t('none_found')}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
parseMessage(msg: WebSocketJsonResponse) {
|
||||||
|
console.log(msg);
|
||||||
|
let res = wsJsonToRes(msg);
|
||||||
|
if (msg.error) {
|
||||||
|
toast(i18n.t(msg.error), 'danger');
|
||||||
|
return;
|
||||||
|
} else if (res.op == UserOperation.GetSite) {
|
||||||
|
let data = res.data as GetSiteResponse;
|
||||||
|
this.state.siteRes = data;
|
||||||
|
this.state.loading = false;
|
||||||
|
this.setState(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
ui/src/components/main.tsx
vendored
1
ui/src/components/main.tsx
vendored
|
@ -114,6 +114,7 @@ export class Main extends Component<any, MainState> {
|
||||||
banned: [],
|
banned: [],
|
||||||
online: null,
|
online: null,
|
||||||
version: null,
|
version: null,
|
||||||
|
federated_instances: null,
|
||||||
},
|
},
|
||||||
showEditSite: false,
|
showEditSite: false,
|
||||||
loading: true,
|
loading: true,
|
||||||
|
|
1
ui/src/components/navbar.tsx
vendored
1
ui/src/components/navbar.tsx
vendored
|
@ -82,6 +82,7 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
banned: [],
|
banned: [],
|
||||||
online: null,
|
online: null,
|
||||||
version: null,
|
version: null,
|
||||||
|
federated_instances: null,
|
||||||
},
|
},
|
||||||
searchParam: '',
|
searchParam: '',
|
||||||
toggleSearch: false,
|
toggleSearch: false,
|
||||||
|
|
1
ui/src/components/post.tsx
vendored
1
ui/src/components/post.tsx
vendored
|
@ -97,6 +97,7 @@ export class Post extends Component<any, PostState> {
|
||||||
},
|
},
|
||||||
online: null,
|
online: null,
|
||||||
version: null,
|
version: null,
|
||||||
|
federated_instances: undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
1
ui/src/components/user.tsx
vendored
1
ui/src/components/user.tsx
vendored
|
@ -142,6 +142,7 @@ export class User extends Component<any, UserState> {
|
||||||
},
|
},
|
||||||
version: undefined,
|
version: undefined,
|
||||||
my_user: undefined,
|
my_user: undefined,
|
||||||
|
federated_instances: undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
2
ui/src/index.tsx
vendored
2
ui/src/index.tsx
vendored
|
@ -19,6 +19,7 @@ import { AdminSettings } from './components/admin-settings';
|
||||||
import { Inbox } from './components/inbox';
|
import { Inbox } from './components/inbox';
|
||||||
import { Search } from './components/search';
|
import { Search } from './components/search';
|
||||||
import { Sponsors } from './components/sponsors';
|
import { Sponsors } from './components/sponsors';
|
||||||
|
import { Instances } from './components/instances';
|
||||||
import { Symbols } from './components/symbols';
|
import { Symbols } from './components/symbols';
|
||||||
import { i18n } from './i18next';
|
import { i18n } from './i18next';
|
||||||
|
|
||||||
|
@ -89,6 +90,7 @@ class Index extends Component<any, any> {
|
||||||
path={`/password_change/:token`}
|
path={`/password_change/:token`}
|
||||||
component={PasswordChange}
|
component={PasswordChange}
|
||||||
/>
|
/>
|
||||||
|
<Route path={`/instances`} component={Instances} />
|
||||||
</Switch>
|
</Switch>
|
||||||
<Symbols />
|
<Symbols />
|
||||||
</div>
|
</div>
|
||||||
|
|
1
ui/src/interfaces.ts
vendored
1
ui/src/interfaces.ts
vendored
|
@ -867,6 +867,7 @@ export interface GetSiteResponse {
|
||||||
online: number;
|
online: number;
|
||||||
version: string;
|
version: string;
|
||||||
my_user?: User;
|
my_user?: User;
|
||||||
|
federated_instances: Array<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SiteResponse {
|
export interface SiteResponse {
|
||||||
|
|
5
ui/translations/en.json
vendored
5
ui/translations/en.json
vendored
|
@ -294,5 +294,8 @@
|
||||||
"invalid_post_title": "Invalid post title",
|
"invalid_post_title": "Invalid post title",
|
||||||
"invalid_url": "Invalid URL.",
|
"invalid_url": "Invalid URL.",
|
||||||
"play_captcha_audio": "Play Captcha Audio",
|
"play_captcha_audio": "Play Captcha Audio",
|
||||||
"bio": "Bio"
|
"bio": "Bio",
|
||||||
|
"instances": "Instances",
|
||||||
|
"linked_instances": "Linked Instances",
|
||||||
|
"none_found": "None found."
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue