Multi instance #26
|
@ -6,7 +6,7 @@ open System.Text.Json.Serialization
|
||||||
/// The variables we need from the account information we get from Mastodon
|
/// The variables we need from the account information we get from Mastodon
|
||||||
[<NoComparison; NoEquality; AllowNullLiteral>]
|
[<NoComparison; NoEquality; AllowNullLiteral>]
|
||||||
type MastodonAccount () =
|
type MastodonAccount () =
|
||||||
/// The user name (what we store as naUser)
|
/// The user name (what we store as mastodonUser)
|
||||||
[<JsonPropertyName "username">]
|
[<JsonPropertyName "username">]
|
||||||
member val Username = "" with get, set
|
member val Username = "" with get, set
|
||||||
/// The account name; will generally be the same as username for local accounts, which is all we can verify
|
/// The account name; will generally be the same as username for local accounts, which is all we can verify
|
||||||
|
|
|
@ -6,6 +6,7 @@ open JobsJobsJobs.Domain.Types
|
||||||
open Polly
|
open Polly
|
||||||
open RethinkDb.Driver
|
open RethinkDb.Driver
|
||||||
open RethinkDb.Driver.Net
|
open RethinkDb.Driver.Net
|
||||||
|
open RethinkDb.Driver.Ast
|
||||||
|
|
||||||
/// Shorthand for the RethinkDB R variable (how every command starts)
|
/// Shorthand for the RethinkDB R variable (how every command starts)
|
||||||
let private r = RethinkDB.R
|
let private r = RethinkDB.R
|
||||||
|
@ -166,10 +167,20 @@ module Startup =
|
||||||
log.LogInformation $"Creating \"{idx}\" index on {table}"
|
log.LogInformation $"Creating \"{idx}\" index on {table}"
|
||||||
r.Table(table).IndexCreate(idx).RunWriteAsync conn |> awaitIgnore)
|
r.Table(table).IndexCreate(idx).RunWriteAsync conn |> awaitIgnore)
|
||||||
}
|
}
|
||||||
do! ensureIndexes Table.Citizen [ "naUser" ]
|
|
||||||
do! ensureIndexes Table.Listing [ "citizenId"; "continentId"; "isExpired" ]
|
do! ensureIndexes Table.Listing [ "citizenId"; "continentId"; "isExpired" ]
|
||||||
do! ensureIndexes Table.Profile [ "continentId" ]
|
do! ensureIndexes Table.Profile [ "continentId" ]
|
||||||
do! ensureIndexes Table.Success [ "citizenId" ]
|
do! ensureIndexes Table.Success [ "citizenId" ]
|
||||||
|
// The instance/user is a compound index
|
||||||
|
let! userIdx = r.Table(Table.Citizen).IndexList().RunResultAsync<string list> conn
|
||||||
|
match userIdx |> List.contains "instanceUser" with
|
||||||
|
| true -> ()
|
||||||
|
| false ->
|
||||||
|
let! _ =
|
||||||
|
r.Table(Table.Citizen)
|
||||||
|
.IndexCreate("instanceUser",
|
||||||
|
ReqlFunction1 (fun row -> upcast r.Array (row.G "instance", row.G "mastodonUser")))
|
||||||
|
.RunWriteAsync conn
|
||||||
|
()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,7 +226,6 @@ let regexContains = System.Text.RegularExpressions.Regex.Escape >> sprintf "(?i)
|
||||||
|
|
||||||
open JobsJobsJobs.Domain
|
open JobsJobsJobs.Domain
|
||||||
open JobsJobsJobs.Domain.SharedTypes
|
open JobsJobsJobs.Domain.SharedTypes
|
||||||
open RethinkDb.Driver.Ast
|
|
||||||
|
|
||||||
/// Profile data access functions
|
/// Profile data access functions
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
@ -287,7 +297,7 @@ module Profile =
|
||||||
.HashMap("displayName",
|
.HashMap("displayName",
|
||||||
r.Branch (it.G("realName" ).Default_("").Ne "", it.G "realName",
|
r.Branch (it.G("realName" ).Default_("").Ne "", it.G "realName",
|
||||||
it.G("displayName").Default_("").Ne "", it.G "displayName",
|
it.G("displayName").Default_("").Ne "", it.G "displayName",
|
||||||
it.G "naUser"))
|
it.G "mastodonUser"))
|
||||||
.With ("citizenId", it.G "id")))
|
.With ("citizenId", it.G "id")))
|
||||||
.Pluck("citizenId", "displayName", "seekingEmployment", "remoteWork", "fullTime", "lastUpdatedOn")
|
.Pluck("citizenId", "displayName", "seekingEmployment", "remoteWork", "fullTime", "lastUpdatedOn")
|
||||||
.OrderBy(ReqlFunction1 (fun it -> upcast it.G("displayName").Downcase ()))
|
.OrderBy(ReqlFunction1 (fun it -> upcast it.G("displayName").Downcase ()))
|
||||||
|
@ -348,10 +358,10 @@ module Citizen =
|
||||||
.RunResultAsync<Citizen>
|
.RunResultAsync<Citizen>
|
||||||
|> withReconnOption conn
|
|> withReconnOption conn
|
||||||
|
|
||||||
/// Find a citizen by their No Agenda Social username
|
/// Find a citizen by their Mastodon username
|
||||||
let findByNaUser (naUser : string) conn =
|
let findByMastodonUser (instance : string) (mastodonUser : string) conn =
|
||||||
r.Table(Table.Citizen)
|
r.Table(Table.Citizen)
|
||||||
.GetAll(naUser).OptArg("index", "naUser").Nth(0)
|
.GetAll(r.Array (instance, mastodonUser)).OptArg("index", "instanceUser").Nth(0)
|
||||||
.RunResultAsync<Citizen>
|
.RunResultAsync<Citizen>
|
||||||
|> withReconnOption conn
|
|> withReconnOption conn
|
||||||
|
|
||||||
|
@ -546,9 +556,17 @@ module Success =
|
||||||
.HashMap("citizenName",
|
.HashMap("citizenName",
|
||||||
r.Branch(it.G("realName" ).Default_("").Ne "", it.G "realName",
|
r.Branch(it.G("realName" ).Default_("").Ne "", it.G "realName",
|
||||||
it.G("displayName").Default_("").Ne "", it.G "displayName",
|
it.G("displayName").Default_("").Ne "", it.G "displayName",
|
||||||
it.G "naUser"))
|
it.G "mastodonUser"))
|
||||||
.With ("hasStory", it.G("story").Default_("").Gt "")))
|
.With ("hasStory", it.G("story").Default_("").Gt "")))
|
||||||
.Pluck("id", "citizenId", "citizenName", "recordedOn", "fromHere", "hasStory")
|
.Pluck("id", "citizenId", "citizenName", "recordedOn", "fromHere", "hasStory")
|
||||||
.OrderBy(r.Desc "recordedOn")
|
.OrderBy(r.Desc "recordedOn")
|
||||||
.RunResultAsync<StoryEntry list>
|
.RunResultAsync<StoryEntry list>
|
||||||
|> withReconn conn
|
|> withReconn conn
|
||||||
|
|
||||||
|
(*
|
||||||
|
-- To replace naUser with mastodonUser for NAS --
|
||||||
|
r.db('jobsjobsjobs').table('citizen').replace(function(it) {
|
||||||
|
return it.merge({ mastodonUser: it('naUser'), instance: 'nas' }).without('naUser');
|
||||||
|
})
|
||||||
|
|
||||||
|
*)
|
||||||
|
|
|
@ -117,22 +117,23 @@ module Citizen =
|
||||||
| Some instance ->
|
| Some instance ->
|
||||||
let log = (logger ctx).CreateLogger (nameof JobsJobsJobs.Api.Auth)
|
let log = (logger ctx).CreateLogger (nameof JobsJobsJobs.Api.Auth)
|
||||||
|
|
||||||
match! Auth.verifyWithMastodon authCode instance cfg.ReturnUrl log with
|
match! Auth.verifyWithMastodon authCode instance cfg.ReturnHost log with
|
||||||
| Ok account ->
|
| Ok account ->
|
||||||
// Step 2 - Find / establish Jobs, Jobs, Jobs account
|
// Step 2 - Find / establish Jobs, Jobs, Jobs account
|
||||||
let now = (clock ctx).GetCurrentInstant ()
|
let now = (clock ctx).GetCurrentInstant ()
|
||||||
let dbConn = conn ctx
|
let dbConn = conn ctx
|
||||||
let! citizen = task {
|
let! citizen = task {
|
||||||
match! Data.Citizen.findByNaUser account.Username dbConn with
|
match! Data.Citizen.findByMastodonUser instance.Abbr account.Username dbConn with
|
||||||
| None ->
|
| None ->
|
||||||
let it : Citizen =
|
let it : Citizen =
|
||||||
{ id = CitizenId.create ()
|
{ id = CitizenId.create ()
|
||||||
naUser = account.Username
|
instance = instance.Abbr
|
||||||
displayName = noneIfEmpty account.DisplayName
|
mastodonUser = account.Username
|
||||||
realName = None
|
displayName = noneIfEmpty account.DisplayName
|
||||||
profileUrl = account.Url
|
realName = None
|
||||||
joinedOn = now
|
profileUrl = account.Url
|
||||||
lastSeenOn = now
|
joinedOn = now
|
||||||
|
lastSeenOn = now
|
||||||
}
|
}
|
||||||
do! Data.Citizen.add it dbConn
|
do! Data.Citizen.add it dbConn
|
||||||
return it
|
return it
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{
|
{
|
||||||
"Rethink": {
|
"Rethink": {
|
||||||
|
"Hostname": "data02.bitbadger.solutions",
|
||||||
|
"Db": "jobsjobsjobs-dev"
|
||||||
},
|
},
|
||||||
"Auth": {
|
"Auth": {
|
||||||
"ReturnHost": "http://localhost:5000",
|
"ReturnHost": "http://localhost:5000",
|
||||||
|
|
|
@ -46,7 +46,7 @@ export function yesOrNo (cond : boolean) : string {
|
||||||
* @returns The citizen's display name
|
* @returns The citizen's display name
|
||||||
*/
|
*/
|
||||||
export function citizenName (cit : Citizen) : string {
|
export function citizenName (cit : Citizen) : string {
|
||||||
return cit.realName ?? cit.displayName ?? cit.naUser
|
return cit.realName ?? cit.displayName ?? cit.mastodonUser
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
export interface Citizen {
|
export interface Citizen {
|
||||||
/** The ID of the user */
|
/** The ID of the user */
|
||||||
id : string
|
id : string
|
||||||
|
/** The abbreviation of the instance where this citizen is based */
|
||||||
|
instance : string
|
||||||
/** The handle by which the user is known on Mastodon */
|
/** The handle by which the user is known on Mastodon */
|
||||||
naUser : string
|
mastodonUser : string
|
||||||
/** The user's display name from Mastodon (updated every login) */
|
/** The user's display name from Mastodon (updated every login) */
|
||||||
displayName : string | undefined
|
displayName : string | undefined
|
||||||
/** The user's real name */
|
/** The user's real name */
|
||||||
|
|
|
@ -121,7 +121,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||||
component: () => import(/* webpackChunkName: "so-long" */ "../views/so-long/DeletionOptions.vue")
|
component: () => import(/* webpackChunkName: "so-long" */ "../views/so-long/DeletionOptions.vue")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/so-long/success",
|
path: "/so-long/success/:url",
|
||||||
name: "DeletionSuccess",
|
name: "DeletionSuccess",
|
||||||
component: () => import(/* webpackChunkName: "so-long" */ "../views/so-long/DeletionSuccess.vue")
|
component: () => import(/* webpackChunkName: "so-long" */ "../views/so-long/DeletionSuccess.vue")
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default createStore({
|
||||||
state: () : State => {
|
state: () : State => {
|
||||||
return {
|
return {
|
||||||
user: undefined,
|
user: undefined,
|
||||||
logOnState: "<em>Welcome back! Verifying your No Agenda Social account…</em>",
|
logOnState: "<em>Welcome back!</em>",
|
||||||
continents: []
|
continents: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,8 +21,8 @@ article
|
||||||
p.
|
p.
|
||||||
Clicking the #[span.link View] link on a listing brings up the full view page for a listing. This page displays all
|
Clicking the #[span.link View] link on a listing brings up the full view page for a listing. This page displays all
|
||||||
of the information from the search results, along with the citizen who posted it, and the full details of the job.
|
of the information from the search results, along with the citizen who posted it, and the full details of the job.
|
||||||
The citizen’s name is a link to their profile page at No Agenda Social; you can use that to get their handle,
|
The citizen’s name is a link to their profile page at their Mastodon instance; you can use that to get their
|
||||||
and use NAS’s communication facilites to inquire about the position.
|
handle, and use Mastodon’s communication facilites to inquire about the position.
|
||||||
p: em.text-muted.
|
p: em.text-muted.
|
||||||
(If you know of a way to construct a link to Mastodon that would start a direct message, please reach out;
|
(If you know of a way to construct a link to Mastodon that would start a direct message, please reach out;
|
||||||
I’ve searched and searched, and asked NAS, but have not yet determined how to do that.)
|
I’ve searched and searched, and asked NAS, but have not yet determined how to do that.)
|
||||||
|
@ -43,9 +43,9 @@ article
|
||||||
The #[span.link My Job Listings] page will show you all of your active job listings just below the
|
The #[span.link My Job Listings] page will show you all of your active job listings just below the
|
||||||
#[span.button Add a Job Listing] button. Within this table, you can edit the listing, view it, or expire it (more on
|
#[span.button Add a Job Listing] button. Within this table, you can edit the listing, view it, or expire it (more on
|
||||||
that below). The #[span.link View] link will show you the job listing just as other users will see it. You can share
|
that below). The #[span.link View] link will show you the job listing just as other users will see it. You can share
|
||||||
the link from your browser over on No Agenda Social, and those who click on it will be able to view it. (Existing
|
the link from your browser on any No Agenda-affiliated Mastodon instance, and those who click on it will be able to
|
||||||
users of Jobs, Jobs, Jobs will go right to it; others will need to authorize this site’s access, but then they
|
view it. (Existing users of Jobs, Jobs, Jobs will go right to it; others will need to authorize this site’s
|
||||||
will get there as well.)
|
access, but then they will get there as well.)
|
||||||
|
|
||||||
h5 Expire a Job Listing
|
h5 Expire a Job Listing
|
||||||
p.
|
p.
|
||||||
|
@ -68,7 +68,7 @@ article
|
||||||
The #[span.link Employment Profiles] link at the side allows you to search for profiles by continent, the
|
The #[span.link Employment Profiles] link at the side allows you to search for profiles by continent, the
|
||||||
citizen’s desire for remote work, a skill, or any text in their professional biography and experience. If you
|
citizen’s desire for remote work, a skill, or any text in their professional biography and experience. If you
|
||||||
find someone with whom you’d like to discuss potential opportunities, the name at the top of the profile links
|
find someone with whom you’d like to discuss potential opportunities, the name at the top of the profile links
|
||||||
to their No Agenda Social account, where you can use its features to get in touch.
|
to their Mastodon profile, where you can use its features to get in touch.
|
||||||
|
|
||||||
hr
|
hr
|
||||||
|
|
||||||
|
@ -76,8 +76,8 @@ article
|
||||||
p.
|
p.
|
||||||
The employment profile is your résumé, visible to other citizens here. It also allows you to specify
|
The employment profile is your résumé, visible to other citizens here. It also allows you to specify
|
||||||
your real name, if you so desire; if that is filled in, that is how you will be identified in search results,
|
your real name, if you so desire; if that is filled in, that is how you will be identified in search results,
|
||||||
profile views, etc. If not, you will be identified as you are on No Agenda Social; this system updates your current
|
profile views, etc. If not, you will be identified as you are on your Mastodon instance; this system updates your
|
||||||
display name each time you log on.
|
current display name each time you log on.
|
||||||
|
|
||||||
h5 Completing Your Profile
|
h5 Completing Your Profile
|
||||||
p.
|
p.
|
||||||
|
@ -99,19 +99,19 @@ article
|
||||||
li.
|
li.
|
||||||
If you check the #[span.link Allow my profile to be searched publicly] checkbox #[strong and] you are seeking
|
If you check the #[span.link Allow my profile to be searched publicly] checkbox #[strong and] you are seeking
|
||||||
employment, your continent, region, and skills fields will be searchable and displayed to public users of the
|
employment, your continent, region, and skills fields will be searchable and displayed to public users of the
|
||||||
site. They will not be tied to your No Agenda Social handle or real name; they are there to let people peek
|
site. They will not be tied to your Mastodon handle or real name; they are there to let people peek behind the
|
||||||
behind the curtain a bit, and hopefully inspire them to join us.
|
curtain a bit, and hopefully inspire them to join us.
|
||||||
|
|
||||||
h5 Viewing and Sharing Your Profile
|
h5 Viewing and Sharing Your Profile
|
||||||
p.
|
p.
|
||||||
Once your profile has been established, the #[span.link My Employment Profile] page will have a button at the bottom
|
Once your profile has been established, the #[span.link My Employment Profile] page will have a button at the bottom
|
||||||
that will let you view your profile the way all other validated users will be able to see it. (There will also be a
|
that will let you view your profile the way all other validated users will be able to see it. (There will also be a
|
||||||
link to this page from the #[span.link Dashboard].) The URL of this page can be shared on No Agenda Social, if you
|
link to this page from the #[span.link Dashboard].) The URL of this page can be shared on any No Agenda-affiliated
|
||||||
would like to share it there. Just as with job listings, existing users will go straight there, while other No
|
Mastodon instance, if you would like to share it there. Just as with job listings, existing users will go straight
|
||||||
Agenda Social users will get there once they authorize this application.
|
there, while others will get there once they authorize this application.
|
||||||
p.
|
p.
|
||||||
The name on employment profiles is a link to that user’s profile on No Agenda Social; from there, others can
|
The name on employment profiles is a link to that user’s profile on their Mastodon instance; from there,
|
||||||
communicate further with you using the tools Mastodon provides.
|
others can communicate further with you using the tools Mastodon provides.
|
||||||
|
|
||||||
h5 “I Found a Job!”
|
h5 “I Found a Job!”
|
||||||
p.
|
p.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
article
|
article
|
||||||
page-title(title="Privacy Policy")
|
page-title(title="Privacy Policy")
|
||||||
h3 Privacy Policy
|
h3 Privacy Policy
|
||||||
p: em (as of February 6#[sup th], 2021)
|
p: em (as of September 6#[sup th], 2021)
|
||||||
|
|
||||||
p.
|
p.
|
||||||
{{name}} (“we,” “our,” or “us”) is committed to protecting your privacy. This
|
{{name}} (“we,” “our,” or “us”) is committed to protecting your privacy. This
|
||||||
|
@ -58,7 +58,7 @@ article
|
||||||
li Name / Username
|
li Name / Username
|
||||||
li Coarse Geographic Location
|
li Coarse Geographic Location
|
||||||
li Employment History
|
li Employment History
|
||||||
li No Agenda Social Account Name / Profile
|
li Mastodon Account Name / Profile
|
||||||
|
|
||||||
h4 How Do We Use The Information We Collect?
|
h4 How Do We Use The Information We Collect?
|
||||||
p Any of the information we collect from you may be used in one of the following ways:
|
p Any of the information we collect from you may be used in one of the following ways:
|
||||||
|
@ -75,9 +75,9 @@ article
|
||||||
p {{name}} will collect End User Data necessary to provide the {{name}} services to our customers.
|
p {{name}} will collect End User Data necessary to provide the {{name}} services to our customers.
|
||||||
p.
|
p.
|
||||||
End users may voluntarily provide us with information they have made available on social media websites
|
End users may voluntarily provide us with information they have made available on social media websites
|
||||||
(specifically No Agenda Social). If you provide us with any such information, we may collect publicly available
|
(specifically No Agenda-affiliated Mastodon instances). If you provide us with any such information, we may collect
|
||||||
information from the social media websites you have indicated. You can control how much of your information social
|
publicly available information from the social media websites you have indicated. You can control how much of your
|
||||||
media websites make public by visiting these websites and changing your privacy settings.
|
information social media websites make public by visiting these websites and changing your privacy settings.
|
||||||
|
|
||||||
h4 When does {{name}} use customer information from third parties?
|
h4 When does {{name}} use customer information from third parties?
|
||||||
p We do not utilize third party information apart from the end-user data described above.
|
p We do not utilize third party information apart from the end-user data described above.
|
||||||
|
@ -223,10 +223,10 @@ article
|
||||||
|
|
||||||
h4 Tracking Technologies
|
h4 Tracking Technologies
|
||||||
p.
|
p.
|
||||||
{{name}} does not use any tracking technologies. When an authorization code is received from No Agenda Social, that
|
{{name}} does not use any tracking technologies. When an authorization code is received from Mastodon, that token is
|
||||||
token is stored in the browser’s memory, and the Service uses tokens on each request for data. If the page is
|
stored in the browser’s memory, and the Service uses tokens on each request for data. If the page is refreshed
|
||||||
refreshed or the browser window/tab is closed, this token disappears, and a new one must be generated before the
|
or the browser window/tab is closed, this token disappears, and a new one must be generated before the application
|
||||||
application can be used again.
|
can be used again.
|
||||||
|
|
||||||
h4 Information about General Data Protection Regulation (GDPR)
|
h4 Information about General Data Protection Regulation (GDPR)
|
||||||
p.
|
p.
|
||||||
|
@ -335,6 +335,12 @@ article
|
||||||
h4 Contact Us
|
h4 Contact Us
|
||||||
p Don’t hesitate to contact us if you have any questions.
|
p Don’t hesitate to contact us if you have any questions.
|
||||||
ul: li Via this Link: #[router-link(to="/how-it-works") https://noagendacareers.com/how-it-works]
|
ul: li Via this Link: #[router-link(to="/how-it-works") https://noagendacareers.com/how-it-works]
|
||||||
|
|
||||||
|
hr
|
||||||
|
|
||||||
|
p: em.
|
||||||
|
Change on September 6#[sup th], 2021 – replaced “No Agenda Social” with generic terms for any
|
||||||
|
authorized Mastodon instance.
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
article
|
article
|
||||||
page-title(title="Terms of Service")
|
page-title(title="Terms of Service")
|
||||||
h3 Terms of Service
|
h3 Terms of Service
|
||||||
p: em (as of February 6#[sup th], 2021)
|
p: em (as of September 6#[sup th], 2021)
|
||||||
|
|
||||||
h4 Acceptance of Terms
|
h4 Acceptance of Terms
|
||||||
p.
|
p.
|
||||||
|
@ -11,12 +11,20 @@ article
|
||||||
acceptance of these terms.
|
acceptance of these terms.
|
||||||
|
|
||||||
h4 Description of Service and Registration
|
h4 Description of Service and Registration
|
||||||
p.
|
p
|
||||||
Jobs, Jobs, Jobs is a service that allows individuals to enter and amend employment profiles, restricting access
|
| Jobs, Jobs, Jobs is a service that allows individuals to enter and amend employment profiles, restricting access
|
||||||
to the details of these profiles to other users of
|
| to the details of these profiles to other users of
|
||||||
#[a(href="https://noagendasocial.com" target="_blank") No Agenda Social]. Registration is accomplished by allowing
|
= " "
|
||||||
Jobs, Jobs, Jobs to read one’s No Agenda Social profile. See our
|
template(v-for="(it, idx) in instances" :key="idx")
|
||||||
#[router-link(to="/privacy-policy") privacy policy] for details on the personal (user) information we maintain.
|
a(:href="it.url" target="_blank") {{it.name}}
|
||||||
|
template(v-if="idx + 2 < instances.length")= ", "
|
||||||
|
template(v-else-if="idx + 1 < instances.length")= ", and "
|
||||||
|
template(v-else)= ". "
|
||||||
|
| Registration is accomplished by allowing Jobs, Jobs, Jobs to read one’s Mastodon profile. See our
|
||||||
|
= " "
|
||||||
|
router-link(to="/privacy-policy") privacy policy
|
||||||
|
= " "
|
||||||
|
| for details on the personal (user) information we maintain.
|
||||||
|
|
||||||
h4 Liability
|
h4 Liability
|
||||||
p.
|
p.
|
||||||
|
@ -34,4 +42,30 @@ article
|
||||||
p.
|
p.
|
||||||
You may also wish to review our #[router-link(to="/privacy-policy") privacy policy] to learn how we handle your
|
You may also wish to review our #[router-link(to="/privacy-policy") privacy policy] to learn how we handle your
|
||||||
data.
|
data.
|
||||||
|
|
||||||
|
hr
|
||||||
|
|
||||||
|
p: em.
|
||||||
|
Change on September 6#[sup th], 2021 – replaced “No Agenda Social” with a list of all No
|
||||||
|
Agenda-affiliated Mastodon instances.
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, Ref, ref } from "vue"
|
||||||
|
|
||||||
|
import api, { Instance } from "@/api"
|
||||||
|
import { toastError } from "@/components/layout/AppToaster.vue"
|
||||||
|
|
||||||
|
const instances : Ref<Instance[]> = ref([])
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const apiResp = await api.instances.all()
|
||||||
|
if (typeof apiResp === "string") {
|
||||||
|
toastError(apiResp, "retrieving instances")
|
||||||
|
} else if (typeof apiResp === "undefined") {
|
||||||
|
toastError("No instances to display", undefined)
|
||||||
|
} else {
|
||||||
|
instances.value = apiResp
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
|
@ -30,6 +30,7 @@ const logOn = async () => {
|
||||||
} else if (typeof instance === "undefined") {
|
} else if (typeof instance === "undefined") {
|
||||||
setMessage(`Mastodon instance ${abbr} not found`)
|
setMessage(`Mastodon instance ${abbr} not found`)
|
||||||
} else {
|
} else {
|
||||||
|
setMessage(`<em>Welcome back! Verifying your ${instance.name} account…</em>`)
|
||||||
const code = route.query.code
|
const code = route.query.code
|
||||||
if (code) {
|
if (code) {
|
||||||
await store.dispatch("logOn", { abbr, code })
|
await store.dispatch("logOn", { abbr, code })
|
||||||
|
|
|
@ -24,7 +24,6 @@ const selected : Ref<Instance | undefined> = ref(undefined)
|
||||||
/** The authorization URL to which the user should be directed */
|
/** The authorization URL to which the user should be directed */
|
||||||
const authUrl = computed(() => {
|
const authUrl = computed(() => {
|
||||||
if (selected.value) {
|
if (selected.value) {
|
||||||
/** The client ID for Jobs, Jobs, Jobs at No Agenda Social */
|
|
||||||
const client = `client_id=${selected.value.clientId}`
|
const client = `client_id=${selected.value.clientId}`
|
||||||
const scope = "scope=read:accounts"
|
const scope = "scope=read:accounts"
|
||||||
const redirect = `redirect_uri=${document.location.origin}/citizen/${selected.value.abbr}/authorized`
|
const redirect = `redirect_uri=${document.location.origin}/citizen/${selected.value.abbr}/authorized`
|
||||||
|
@ -35,7 +34,7 @@ const authUrl = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a given Mastadon instance
|
* Select a given Mastodon instance
|
||||||
*
|
*
|
||||||
* @param abbr The abbreviation of the instance being selected
|
* @param abbr The abbreviation of the instance being selected
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -66,7 +66,7 @@ const title = computed(() => it.value ? `${it.value.listing.title} | Job Listing
|
||||||
const details = computed(() => toHtml(it.value?.listing.text ?? ""))
|
const details = computed(() => toHtml(it.value?.listing.text ?? ""))
|
||||||
|
|
||||||
/** The NAS profile URL for the citizen who posted this job listing */
|
/** The NAS profile URL for the citizen who posted this job listing */
|
||||||
const profileUrl = computed(() => citizen.value ? `https://noagendasocial.com/@${citizen.value.naUser}` : "")
|
const profileUrl = computed(() => citizen.value ? citizen.value.profileUrl : "")
|
||||||
|
|
||||||
/** The needed by date, formatted in SHOUTING MODE */
|
/** The needed by date, formatted in SHOUTING MODE */
|
||||||
const neededBy = (nb : string) => formatNeededBy(nb).toUpperCase()
|
const neededBy = (nb : string) => formatNeededBy(nb).toUpperCase()
|
||||||
|
|
|
@ -13,10 +13,10 @@ article
|
||||||
p.
|
p.
|
||||||
This option will make it like you never visited this site. It will delete your profile, skills, success stories, and
|
This option will make it like you never visited this site. It will delete your profile, skills, success stories, and
|
||||||
account. This is what you want to use if you want to disappear from this application. Clicking the button below
|
account. This is what you want to use if you want to disappear from this application. Clicking the button below
|
||||||
#[strong will not] affect your No Agenda Social account in any way; its effects are limited to Jobs, Jobs, Jobs.
|
#[strong will not] affect your Mastodon account in any way; its effects are limited to Jobs, Jobs, Jobs.
|
||||||
p: em.
|
p: em.
|
||||||
(This will not revoke this application’s permissions on No Agenda Social; you will have to remove this
|
(This will not revoke this application’s permissions on Mastodon; you will have to remove this yourself. The
|
||||||
yourself. The confirmation message has a link where you can do this; once the page loads, find the
|
confirmation message has a link where you can do this; once the page loads, find the
|
||||||
#[strong Jobs, Jobs, Jobs] entry, and click the #[strong × Revoke] link for that entry.)
|
#[strong Jobs, Jobs, Jobs] entry, and click the #[strong × Revoke] link for that entry.)
|
||||||
p.text-center: button.btn.btn-danger(@click.prevent="deleteAccount") Delete Your Entire Account
|
p.text-center: button.btn.btn-danger(@click.prevent="deleteAccount") Delete Your Entire Account
|
||||||
</template>
|
</template>
|
||||||
|
@ -32,9 +32,12 @@ import { useStore } from "@/store"
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
/** The currently logged-on user */
|
||||||
|
const user = store.state.user as LogOnSuccess
|
||||||
|
|
||||||
/** Delete the profile only; redirect to home page on success */
|
/** Delete the profile only; redirect to home page on success */
|
||||||
const deleteProfile = async () => {
|
const deleteProfile = async () => {
|
||||||
const resp = await api.profile.delete(store.state.user as LogOnSuccess)
|
const resp = await api.profile.delete(user)
|
||||||
if (typeof resp === "string") {
|
if (typeof resp === "string") {
|
||||||
toastError(resp, "Deleting Profile")
|
toastError(resp, "Deleting Profile")
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,13 +48,27 @@ const deleteProfile = async () => {
|
||||||
|
|
||||||
/** Delete everything pertaining to the user's account */
|
/** Delete everything pertaining to the user's account */
|
||||||
const deleteAccount = async () => {
|
const deleteAccount = async () => {
|
||||||
const resp = await api.citizen.delete(store.state.user as LogOnSuccess)
|
const citizenResp = await api.citizen.retrieve(user.citizenId, user)
|
||||||
if (typeof resp === "string") {
|
if (typeof citizenResp === "string") {
|
||||||
toastError(resp, "Deleting Account")
|
toastError(citizenResp, "retrieving citizen")
|
||||||
|
} else if (typeof citizenResp === "undefined") {
|
||||||
|
toastError("Could not retrieve citizen record", undefined)
|
||||||
} else {
|
} else {
|
||||||
store.commit("clearUser")
|
const instResp = await api.instances.byAbbr(citizenResp.instance)
|
||||||
toastSuccess("Account Deleted Successfully")
|
if (typeof instResp === "string") {
|
||||||
router.push("/so-long/success")
|
toastError(instResp, "retriving instance")
|
||||||
|
} else if (typeof instResp === "undefined") {
|
||||||
|
toastError("Could not retrieve instance", undefined)
|
||||||
|
} else {
|
||||||
|
const resp = await api.citizen.delete(user)
|
||||||
|
if (typeof resp === "string") {
|
||||||
|
toastError(resp, "Deleting Account")
|
||||||
|
} else {
|
||||||
|
store.commit("clearUser")
|
||||||
|
toastSuccess("Account Deleted Successfully")
|
||||||
|
router.push(`/so-long/success/${instResp.url}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,8 +4,18 @@ article
|
||||||
h3.pb-3 Account Deletion Success
|
h3.pb-3 Account Deletion Success
|
||||||
p.
|
p.
|
||||||
Your account has been successfully deleted. To revoke the permissions you have previously granted to this
|
Your account has been successfully deleted. To revoke the permissions you have previously granted to this
|
||||||
application, find it in #[a(href="https://noagendasocial.com/oauth/authorized_applications") this list] and click
|
application, find it in #[a(:href="`${url}/oauth/authorized_applications`") this list] and click
|
||||||
#[strong × Revoke]. Otherwise, clicking “Log On” in the left-hand menu will create a new, empty
|
#[strong × Revoke]. Otherwise, clicking “Log On” in the left-hand menu will create a new, empty
|
||||||
account without prompting you further.
|
account without prompting you further.
|
||||||
p Thank you for participating, and thank you for your courage. #GitmoNation
|
p Thank you for participating, and thank you for your courage. #GitmoNation
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useRoute } from "vue-router"
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
/** The URL of the instance from which the deleted user had authorized access */
|
||||||
|
const url = route.params.url as string
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
|
@ -33,7 +33,7 @@ module CitizenId =
|
||||||
module Citizen =
|
module Citizen =
|
||||||
/// Get the name of the citizen (the first of real name, display name, or handle that is filled in)
|
/// Get the name of the citizen (the first of real name, display name, or handle that is filled in)
|
||||||
let name x =
|
let name x =
|
||||||
[ x.realName; x.displayName; Some x.naUser ]
|
[ x.realName; x.displayName; Some x.mastodonUser ]
|
||||||
|> List.find Option.isSome
|
|> List.find Option.isSome
|
||||||
|> Option.get
|
|> Option.get
|
||||||
|
|
||||||
|
|
|
@ -92,8 +92,8 @@ type MastodonInstance () =
|
||||||
|
|
||||||
/// The authorization options for Jobs, Jobs, Jobs
|
/// The authorization options for Jobs, Jobs, Jobs
|
||||||
type AuthOptions () =
|
type AuthOptions () =
|
||||||
/// The return URL for Mastodoon verification
|
/// The host for the return URL for Mastodoon verification
|
||||||
member val ReturnUrl = "" with get, set
|
member val ReturnHost = "" with get, set
|
||||||
/// The secret with which the server signs the JWTs for auth once we've verified with Mastodon
|
/// The secret with which the server signs the JWTs for auth once we've verified with Mastodon
|
||||||
member val ServerSecret = "" with get, set
|
member val ServerSecret = "" with get, set
|
||||||
/// The instances configured for use
|
/// The instances configured for use
|
||||||
|
|
|
@ -13,19 +13,21 @@ type CitizenId = CitizenId of Guid
|
||||||
[<CLIMutable; NoComparison; NoEquality>]
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
type Citizen = {
|
type Citizen = {
|
||||||
/// The ID of the user
|
/// The ID of the user
|
||||||
id : CitizenId
|
id : CitizenId
|
||||||
|
/// The Mastodon instance abbreviation from which this citizen is authorized
|
||||||
|
instance : string
|
||||||
/// The handle by which the user is known on Mastodon
|
/// The handle by which the user is known on Mastodon
|
||||||
naUser : string
|
mastodonUser : string
|
||||||
/// The user's display name from Mastodon (updated every login)
|
/// The user's display name from Mastodon (updated every login)
|
||||||
displayName : string option
|
displayName : string option
|
||||||
/// The user's real name
|
/// The user's real name
|
||||||
realName : string option
|
realName : string option
|
||||||
/// The URL for the user's Mastodon profile
|
/// The URL for the user's Mastodon profile
|
||||||
profileUrl : string
|
profileUrl : string
|
||||||
/// When the user joined Jobs, Jobs, Jobs
|
/// When the user joined Jobs, Jobs, Jobs
|
||||||
joinedOn : Instant
|
joinedOn : Instant
|
||||||
/// When the user last logged in
|
/// When the user last logged in
|
||||||
lastSeenOn : Instant
|
lastSeenOn : Instant
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user